├── .gitignore ├── .gitmodules ├── .swiftlint.yml ├── Configs └── Secrets.swift.example ├── KsApi.playground ├── Contents.swift └── contents.xcplayground ├── KsApi.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── KsApi-TestHelpers-iOS.xcscheme │ ├── KsApi-TestHelpers-tvOS.xcscheme │ ├── KsApi-iOS.xcscheme │ └── KsApi-tvOS.xcscheme ├── KsApi ├── Info.plist ├── KsApi.h ├── MockService.swift ├── ServerConfig.swift ├── Service.swift ├── ServiceTests.swift ├── ServiceType.swift ├── ServiceTypeTests.swift ├── extensions │ └── NSURLSession.swift ├── lib │ ├── Decodable.swift │ ├── EncodableTests.swift │ ├── EncodableType.swift │ ├── Method.swift │ ├── MimeType.swift │ ├── Route.swift │ └── auth │ │ ├── BasicHTTPAuth.swift │ │ ├── ClientAuth.swift │ │ └── OauthToken.swift └── models │ ├── AccessTokenEnvelope.swift │ ├── Activity.swift │ ├── ActivityEnvelope.swift │ ├── ActivityTests.swift │ ├── Backing.swift │ ├── BackingTests.swift │ ├── CategoriesEnvelope.swift │ ├── Category.swift │ ├── CategoryTests.swift │ ├── ChangePaymentMethodEnvelope.swift │ ├── ChangePaymentMethodEnvelopeTests.swift │ ├── CheckoutEnvelope.swift │ ├── CheckoutEnvelopeTests.swift │ ├── Comment.swift │ ├── CommentTests.swift │ ├── CommentsEnvelope.swift │ ├── Config.swift │ ├── ConfigTests.swift │ ├── CreatePledgeEnvelope.swift │ ├── CreatePledgeEnvelopeTests.swift │ ├── DiscoveryEnvelope.swift │ ├── DiscoveryParams.swift │ ├── DiscoveryParamsTests.swift │ ├── ErrorEnvelope.swift │ ├── ErrorEnvelopeTests.swift │ ├── FindFriendsEnvelope.swift │ ├── FindFriendsEnvelopeTests.swift │ ├── FriendStatsEnvelope.swift │ ├── FriendStatsEnvelopeTests.swift │ ├── Item.swift │ ├── ItemTests.swift │ ├── Location.swift │ ├── LocationTests.swift │ ├── Message.swift │ ├── MessageSubject.swift │ ├── MessageTests.swift │ ├── MessageThread.swift │ ├── MessageThreadEnvelope.swift │ ├── MessageThreadTests.swift │ ├── MessageThreadsEnvelope.swift │ ├── Param.swift │ ├── Project.Country.swift │ ├── Project.CountryTests.swift │ ├── Project.PhotoTests.swift │ ├── Project.Video.swift │ ├── Project.VideoTests.swift │ ├── Project.swift │ ├── ProjectActivityEnvelope.swift │ ├── ProjectNotification.swift │ ├── ProjectStatsEnvelope.swift │ ├── ProjectStatsEnvelopeTests.swift │ ├── ProjectTests.swift │ ├── ProjectsEnvelope.swift │ ├── PushEnvelope.swift │ ├── PushEnvelopeTests.swift │ ├── Reward.swift │ ├── RewardTests.swift │ ├── RewardsItem.swift │ ├── RewardsItemTests.swift │ ├── ShippingRule.swift │ ├── ShippingRulesEnvelope.swift │ ├── StarEnvelope.swift │ ├── SubmitApplePayEnvelope.swift │ ├── SubmitApplePayEnvelopeTests.swift │ ├── SurveyResponse.swift │ ├── SurveyResponseTests.swift │ ├── Update.swift │ ├── UpdateDraft.swift │ ├── UpdateDraftTests.swift │ ├── UpdatePledgeEnvelope.swift │ ├── UpdatePledgeEnvelopeTests.swift │ ├── UpdateTests.swift │ ├── User.AvatarTests.swift │ ├── User.NewsletterSubscriptionsTests.swift │ ├── User.NotificationsTests.swift │ ├── User.swift │ ├── UserTests.swift │ ├── VoidEnvelope.swift │ ├── lenses │ ├── Activity.MemberDataLenses.swift │ ├── ActivityLenses.swift │ ├── BackingLenses.swift │ ├── CategoriesEnvelopeLenses.swift │ ├── CategoryLenses.swift │ ├── CheckoutEnvelopeLenses.swift │ ├── CommentLenses.swift │ ├── ConfigLenses.swift │ ├── CreatePledgeEnvelopeLenses.swift │ ├── DiscoveryEnvelopeLenses.swift │ ├── DiscoveryParamsLenses.swift │ ├── FindFriendsEnvelopeLenses.swift │ ├── FriendStatsEnvelope.StatsLenses.swift │ ├── FriendStatsEnvelopeLenses.swift │ ├── ItemLenses.swift │ ├── LocationLenses.swift │ ├── MessageLenses.swift │ ├── MessageThreadLenses.swift │ ├── Project.CreatorDataLenses.swift │ ├── Project.DatesLenses.swift │ ├── Project.MemberDataLenses.swift │ ├── Project.PersonalizationLenses.swift │ ├── Project.PhotoLenses.swift │ ├── Project.StatsLenses.swift │ ├── Project.VideoLenses.swift │ ├── ProjectLenses.swift │ ├── ProjectNotification.ProjectLenses.swift │ ├── ProjectNotificationLenses.swift │ ├── ProjectStatsEnvelope.CumulativeStatsLenses.swift │ ├── ProjectStatsEnvelope.FundingDateStatsLenses.swift │ ├── ProjectStatsEnvelope.ReferrerStatsLenses.swift │ ├── ProjectStatsEnvelope.RewardDistributionLenses.swift │ ├── ProjectStatsEnvelope.VideoStatsLenses.swift │ ├── ProjectStatsEnvelopeLenses.swift │ ├── PushEnvelopeLenses.swift │ ├── Reward.ShippingLenses.swift │ ├── RewardItemLenses.swift │ ├── RewardLenses.swift │ ├── ShippingRuleLenses.swift │ ├── ShippingRulesEnvelopeLenses.swift │ ├── StarEnvelopeLenses.swift │ ├── SubmitApplePayEnvelopeLenses.swift │ ├── SurveyResponseLenses.swift │ ├── UpdateDraftLenses.swift │ ├── UpdateLenses.swift │ ├── User.AvatarLenses.swift │ ├── User.NewsletterSubscriptionsLenses.swift │ ├── User.NotificationsLenses.swift │ ├── User.StatsLenses.swift │ ├── UserLenses.swift │ └── VideoStatsLenses.swift │ └── templates │ ├── ActivityTemplates.swift │ ├── BackingTemplates.swift │ ├── CategoriesEnvelopeTemplates.swift │ ├── CategoryTemplates.swift │ ├── ChangePaymentMethodEnvelopeTemplates.swift │ ├── CheckoutEnvelopeTemplates.swift │ ├── CommentTemplates.swift │ ├── CommentsEnvelopeTemplates.swift │ ├── ConfigTemplates.swift │ ├── CreatePledgeEnvelopeTemplates.swift │ ├── DiscoveryEnvelopeTemplates.swift │ ├── FindFriendsEnvelopeTemplates.swift │ ├── FriendStatsEnvelopeTemplates.swift │ ├── ItemTemplates.swift │ ├── LocationTemplates.swift │ ├── MessageTemplates.swift │ ├── MessageThreadTemplates.swift │ ├── Project.PhotoTemplates.swift │ ├── Project.VideoTemplates.swift │ ├── ProjectNotificationTemplates.swift │ ├── ProjectStatsEnvelope.CumulativeStatsTemplates.swift │ ├── ProjectStatsEnvelope.FundingDateStatsTemplates.swift │ ├── ProjectStatsEnvelope.ReferrerStatsTemplates.swift │ ├── ProjectStatsEnvelope.RewardDistributionTemplates.swift │ ├── ProjectStatsEnvelope.VideoStatsTemplates.swift │ ├── ProjectStatsEnvelopeTemplates.swift │ ├── ProjectTemplates.swift │ ├── RewardTemplates.swift │ ├── RewardsItemTemplates.swift │ ├── ShippingRuleTemplates.swift │ ├── ShippingRulesEnvelopeTemplates.swift │ ├── StarEnvelopeTemplates.swift │ ├── SubmitApplePayTemplates.swift │ ├── SurveyResponseTemplates.swift │ ├── UpdateDraftTemplates.swift │ ├── UpdatePledgeEnvelopeTemplates.swift │ ├── UpdateTemplates.swift │ ├── User.AvatarTemplates.swift │ ├── User.MemberDataTemplates.swift │ ├── User.NewsletterSubscriptionsTemplates.swift │ ├── User.NotificationsTemplates.swift │ ├── User.StatsTemplates.swift │ └── UserTemplates.swift ├── LICENSE ├── NOTICE ├── README.md ├── bin ├── bootstrap └── test └── circle.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | Carthage/Cartfile.resolved 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/screenshots 65 | 66 | .DS_Store 67 | 68 | Frameworks/native-secrets 69 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Frameworks/ReactiveExtensions"] 2 | path = Frameworks/ReactiveExtensions 3 | url = https://github.com/kickstarter/Kickstarter-ReactiveExtensions.git 4 | [submodule "Frameworks/Argo"] 5 | path = Frameworks/Argo 6 | url = git://github.com/thoughtbot/Argo.git 7 | [submodule "Frameworks/Curry"] 8 | path = Frameworks/Curry 9 | url = https://github.com/thoughtbot/Curry.git 10 | [submodule "Frameworks/Prelude"] 11 | path = Frameworks/Prelude 12 | url = https://github.com/kickstarter/Kickstarter-Prelude.git 13 | [submodule "Frameworks/Runes"] 14 | path = Frameworks/Runes 15 | url = https://github.com/thoughtbot/Runes.git 16 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - variable_name 3 | - file_length 4 | - function_body_length 5 | - function_parameter_count 6 | - nesting 7 | - trailing_comma 8 | - type_body_length 9 | - type_name 10 | excluded: 11 | - Frameworks 12 | - KsApi.playground/ 13 | line_length: 110 14 | type_body_length: 15 | warning: 300 16 | error: 400 17 | reporter: "xcode" 18 | -------------------------------------------------------------------------------- /Configs/Secrets.swift.example: -------------------------------------------------------------------------------- 1 | public enum Secrets { 2 | public static let isOSS = false 3 | public static let fieldReportEmail = "hello@email.com" 4 | 5 | public enum Api { 6 | public enum Client { 7 | public static let production = "deadbeef" 8 | public static let staging = "beefdead" 9 | } 10 | 11 | public enum Endpoint { 12 | public static let production = "api.com" 13 | public static let staging = "api.staging" 14 | } 15 | } 16 | 17 | public enum BasicHTTPAuth { 18 | public static let username = "usr" 19 | public static let password = "pswd" 20 | } 21 | 22 | public enum Firebase { 23 | public enum Huzza { 24 | public static let apiKey = "" 25 | public static let appName = "" 26 | public static let bundleID = "" 27 | public static let clientID = "" 28 | public static let databaseURL = "" 29 | public static let gcmSenderID = "" 30 | public static let googleAppID = "" 31 | public static let storageBucket = "" 32 | } 33 | } 34 | 35 | public enum HockeyAppId { 36 | public static let beta = "beta" 37 | public static let production = "production" 38 | } 39 | 40 | public enum KoalaEndpoint { 41 | public static let staging = "staging"; 42 | public static let production = "production"; 43 | } 44 | 45 | public enum LiveStreams { 46 | public static let endpoint = "streams" 47 | } 48 | 49 | public enum WebEndpoint { 50 | public static let production = "www.kickstarter.com" 51 | public static let staging = "staging.com" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /KsApi.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import XCPlayground 2 | import KsApi 3 | import Prelude 4 | import ReactiveCocoa 5 | import Result 6 | 7 | XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 8 | 9 | let service = Service.init( 10 | serverConfig: ServerConfig.production, 11 | //oauthToken: OauthToken.init(token: "uncomment and put in your token!"), 12 | language: "en" 13 | ) 14 | 15 | let categories = service.fetchCategories() 16 | .flatMap(.Concat) { SignalProducer(values: $0) } 17 | .filter { c in c.isRoot } 18 | .replayLazily(1) 19 | 20 | categories 21 | .map { c in c.name } 22 | .startWithNext { c in 23 | print("Root category ----> \(c)") 24 | } 25 | 26 | // Get the most popular project in each category above. 27 | categories 28 | .map { 29 | DiscoveryParams.defaults 30 | |> DiscoveryParams.lens.category .~ $0 31 | <> DiscoveryParams.lens.sort .~ .Popular 32 | <> DiscoveryParams.lens.perPage .~ 1 33 | } 34 | .flatMap(.Merge, transform: service.fetchProject) 35 | .map { p in p.name } 36 | .startWithNext { name in 37 | print("Project -----> \(name)") 38 | } 39 | 40 | // Get a few recent activities and print out the name of the user that created the activity. 41 | // This only works if you uncomment `oauthToken` above and put in a valid token. 42 | service.fetchActivities() 43 | .flatMap(.Concat) { SignalProducer(values: $0.activities) } 44 | .map { $0.user?.name } 45 | .skipNil() 46 | .startWithNext { name in 47 | print("Activity user's name: \(name)") 48 | } 49 | -------------------------------------------------------------------------------- /KsApi.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /KsApi.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /KsApi.xcodeproj/xcshareddata/xcschemes/KsApi-TestHelpers-iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /KsApi.xcodeproj/xcshareddata/xcschemes/KsApi-TestHelpers-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /KsApi/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /KsApi/KsApi.h: -------------------------------------------------------------------------------- 1 | // 2 | // KsApi.h 3 | // KsApi 4 | // 5 | // Created by Brandon Williams on 9/23/15. 6 | // Copyright © 2015 Kickstarter. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for KsApi. 12 | FOUNDATION_EXPORT double KsApiVersionNumber; 13 | 14 | //! Project version string for KsApi. 15 | FOUNDATION_EXPORT const unsigned char KsApiVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /KsApi/ServerConfig.swift: -------------------------------------------------------------------------------- 1 | /** 2 | A type that knows the location of a Kickstarter API and web server. 3 | */ 4 | public protocol ServerConfigType { 5 | var apiBaseUrl: URL { get } 6 | var webBaseUrl: URL { get } 7 | var apiClientAuth: ClientAuthType { get } 8 | var basicHTTPAuth: BasicHTTPAuthType? { get } 9 | } 10 | 11 | public func == (lhs: ServerConfigType, rhs: ServerConfigType) -> Bool { 12 | return 13 | type(of: lhs) == type(of: rhs) && 14 | lhs.apiBaseUrl == rhs.apiBaseUrl && 15 | lhs.webBaseUrl == rhs.webBaseUrl && 16 | lhs.apiClientAuth == rhs.apiClientAuth && 17 | lhs.basicHTTPAuth == rhs.basicHTTPAuth 18 | } 19 | 20 | public struct ServerConfig: ServerConfigType { 21 | public let apiBaseUrl: URL 22 | public let webBaseUrl: URL 23 | public let apiClientAuth: ClientAuthType 24 | public let basicHTTPAuth: BasicHTTPAuthType? 25 | 26 | public static let production: ServerConfigType = ServerConfig( 27 | apiBaseUrl: URL(string: "https://\(Secrets.Api.Endpoint.production)")!, 28 | webBaseUrl: URL(string: "https://\(Secrets.WebEndpoint.production)")!, 29 | apiClientAuth: ClientAuth.production, 30 | basicHTTPAuth: nil 31 | ) 32 | 33 | public static let staging: ServerConfigType = ServerConfig( 34 | apiBaseUrl: URL(string: "https://\(Secrets.Api.Endpoint.staging)")!, 35 | webBaseUrl: URL(string: "https://\(Secrets.WebEndpoint.staging)")!, 36 | apiClientAuth: ClientAuth.development, 37 | basicHTTPAuth: BasicHTTPAuth.development 38 | ) 39 | 40 | public static let local: ServerConfigType = ServerConfig( 41 | apiBaseUrl: URL(string: "http://api.ksr.dev")!, 42 | webBaseUrl: URL(string: "http://ksr.dev")!, 43 | apiClientAuth: ClientAuth.development, 44 | basicHTTPAuth: BasicHTTPAuth.development 45 | ) 46 | 47 | public init(apiBaseUrl: URL, 48 | webBaseUrl: URL, 49 | apiClientAuth: ClientAuthType, 50 | basicHTTPAuth: BasicHTTPAuthType?) { 51 | 52 | self.apiBaseUrl = apiBaseUrl 53 | self.webBaseUrl = webBaseUrl 54 | self.apiClientAuth = apiClientAuth 55 | self.basicHTTPAuth = basicHTTPAuth 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /KsApi/ServiceTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class ServiceTests: XCTestCase { 5 | 6 | func testDefaults() { 7 | XCTAssertTrue(Service().serverConfig == ServerConfig.production) 8 | XCTAssertNil(Service().oauthToken) 9 | XCTAssertEqual(Service().language, "en") 10 | } 11 | 12 | func testEquals() { 13 | let s1 = Service() 14 | let s2 = Service(serverConfig: ServerConfig.staging) 15 | let s3 = Service(oauthToken: OauthToken(token: "deadbeef")) 16 | let s4 = Service(language: "es") 17 | 18 | XCTAssertTrue(s1 == s1) 19 | XCTAssertTrue(s2 == s2) 20 | XCTAssertTrue(s3 == s3) 21 | XCTAssertTrue(s4 == s4) 22 | 23 | XCTAssertFalse(s1 == s2) 24 | XCTAssertFalse(s1 == s3) 25 | XCTAssertFalse(s1 == s4) 26 | 27 | XCTAssertFalse(s2 == s3) 28 | XCTAssertFalse(s2 == s4) 29 | 30 | XCTAssertFalse(s3 == s4) 31 | } 32 | 33 | func testLogin() { 34 | let loggedOut = Service() 35 | let loggedIn = loggedOut.login(OauthToken(token: "deadbeef")) 36 | 37 | XCTAssertTrue(loggedIn == Service(oauthToken: OauthToken(token: "deadbeef"))) 38 | } 39 | 40 | func testLogout() { 41 | let loggedIn = Service(oauthToken: OauthToken(token: "deadbeef")) 42 | let loggedOut = loggedIn.logout() 43 | 44 | XCTAssertTrue(loggedOut == Service()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /KsApi/lib/Decodable.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | 3 | public extension Decodable { 4 | /** 5 | Decode a JSON dictionary into a `Decoded` type. 6 | 7 | - parameter json: A dictionary with string keys. 8 | 9 | - returns: A decoded value. 10 | */ 11 | public static func decodeJSONDictionary(_ json: [String:Any]) -> Decoded { 12 | return Self.decode(JSON(json)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /KsApi/lib/EncodableTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class EncodableTests: XCTestCase { 5 | 6 | struct EncodableModel: EncodableType { 7 | let id: Int 8 | let name: String 9 | func encode() -> [String:Any] { 10 | return [ 11 | "ID": self.id, 12 | "NAME": self.name 13 | ] 14 | } 15 | } 16 | 17 | func testToJSONString() { 18 | let model = EncodableModel(id: 1, name: "Blob") 19 | XCTAssertEqual(model.toJSONString(), "{\"ID\":1,\"NAME\":\"Blob\"}") 20 | } 21 | 22 | func testToJSONData() { 23 | let model = EncodableModel(id: 1, name: "Blob") 24 | let jsonString = "{\"ID\":1,\"NAME\":\"Blob\"}" 25 | let jsonData = jsonString.data(using: .utf8) 26 | 27 | XCTAssertEqual(model.toJSONData(), jsonData) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/lib/EncodableType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /** 4 | A type that can encode itself into a `[String:Any]` dictionary, usually for then 5 | serializing to a JSON string. 6 | */ 7 | public protocol EncodableType { 8 | func encode() -> [String:Any] 9 | } 10 | 11 | public extension EncodableType { 12 | /** 13 | Returns `NSData` form of encoding. 14 | 15 | - returns: `NSData` 16 | */ 17 | public func toJSONData() -> Data? { 18 | return try? JSONSerialization.data(withJSONObject: encode(), options: []) 19 | } 20 | 21 | /** 22 | Returns `String` form of encoding. 23 | 24 | - returns: `String` 25 | */ 26 | public func toJSONString() -> String? { 27 | return self.toJSONData().flatMap { String(data: $0, encoding: .utf8) } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/lib/Method.swift: -------------------------------------------------------------------------------- 1 | public enum Method: String { 2 | case GET 3 | case POST 4 | case PUT 5 | case DELETE 6 | } 7 | -------------------------------------------------------------------------------- /KsApi/lib/MimeType.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import MobileCoreServices 3 | 4 | extension Data { 5 | internal var imageMime: String? { 6 | 7 | let start = (self as NSData).bytes.bindMemory(to: UInt8.self, capacity: self.count) 8 | 9 | guard let byte: UInt8 = UnsafeBufferPointer(start: start, count: 1).first else { return nil } 10 | 11 | switch byte { 12 | case 0xFF: 13 | return mimeType(uti: kUTTypeJPEG) 14 | case 0x89: 15 | return mimeType(uti: kUTTypePNG) 16 | case 0x47: 17 | return mimeType(uti: kUTTypeGIF) 18 | default: 19 | return nil 20 | } 21 | } 22 | } 23 | 24 | extension URL { 25 | internal var imageMime: String? { 26 | return mimeType(extension: self.pathExtension, where: kUTTypeImage) 27 | } 28 | } 29 | 30 | private func mimeType(extension: String, where: CFString? = nil) -> String? { 31 | let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, 32 | `extension` as CFString, 33 | `where`)?.takeRetainedValue() 34 | return uti.flatMap(mimeType(uti:)) 35 | } 36 | 37 | private func mimeType(uti: CFString) -> String? { 38 | return UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() as String? 39 | } 40 | -------------------------------------------------------------------------------- /KsApi/lib/auth/BasicHTTPAuth.swift: -------------------------------------------------------------------------------- 1 | /** 2 | A type that understands basic HTTP authentication: username and password. 3 | */ 4 | public protocol BasicHTTPAuthType { 5 | var username: String { get } 6 | var password: String { get } 7 | } 8 | 9 | public func == (lhs: BasicHTTPAuthType, rhs: BasicHTTPAuthType) -> Bool { 10 | return type(of: lhs) == type(of: rhs) && 11 | lhs.username == rhs.username && 12 | lhs.password == rhs.password 13 | } 14 | 15 | public func == (lhs: BasicHTTPAuthType?, rhs: BasicHTTPAuthType?) -> Bool { 16 | return type(of: lhs) == type(of: rhs) && 17 | lhs?.username == rhs?.username && 18 | lhs?.password == rhs?.password 19 | } 20 | 21 | extension BasicHTTPAuthType { 22 | /** 23 | Contents of the `Authorization` header needed to perform basic HTTP auth. 24 | */ 25 | var authorizationHeader: String? { 26 | let string = "\(username):\(password)" 27 | if let data = string.data(using: .utf8) { 28 | let base64 = data.base64EncodedString(options: .lineLength64Characters) 29 | return "Basic \(base64)" 30 | } 31 | return nil 32 | } 33 | } 34 | 35 | public struct BasicHTTPAuth: BasicHTTPAuthType { 36 | public let username: String 37 | public let password: String 38 | 39 | public static let development: BasicHTTPAuthType = BasicHTTPAuth( 40 | username: Secrets.BasicHTTPAuth.username, 41 | password: Secrets.BasicHTTPAuth.password 42 | ) 43 | 44 | public init(username: String, password: String) { 45 | self.username = username 46 | self.password = password 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /KsApi/lib/auth/ClientAuth.swift: -------------------------------------------------------------------------------- 1 | /** 2 | A type that holds an API client id, which provides anonymous authentication to the API. 3 | */ 4 | public protocol ClientAuthType { 5 | var clientId: String { get } 6 | } 7 | 8 | public func == (lhs: ClientAuthType, rhs: ClientAuthType) -> Bool { 9 | return type(of: lhs) == type(of: rhs) && 10 | lhs.clientId == rhs.clientId 11 | } 12 | 13 | public struct ClientAuth: ClientAuthType { 14 | public let clientId: String 15 | 16 | public init(clientId: String) { 17 | self.clientId = clientId 18 | } 19 | 20 | public static let production: ClientAuthType = ClientAuth( 21 | clientId: Secrets.Api.Client.production 22 | ) 23 | 24 | public static let development: ClientAuthType = ClientAuth( 25 | clientId: Secrets.Api.Client.staging 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /KsApi/lib/auth/OauthToken.swift: -------------------------------------------------------------------------------- 1 | /** 2 | A type that can provide oauth token authentication, i.e. a user's personal token. 3 | */ 4 | public protocol OauthTokenAuthType { 5 | var token: String { get } 6 | } 7 | 8 | public func == (lhs: OauthTokenAuthType, rhs: OauthTokenAuthType) -> Bool { 9 | return type(of: lhs) == type(of: rhs) && 10 | lhs.token == rhs.token 11 | } 12 | 13 | public func == (lhs: OauthTokenAuthType?, rhs: OauthTokenAuthType?) -> Bool { 14 | return type(of: lhs) == type(of: rhs) && 15 | lhs?.token == rhs?.token 16 | } 17 | 18 | public struct OauthToken: OauthTokenAuthType { 19 | public let token: String 20 | 21 | public init(token: String) { 22 | self.token = token 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /KsApi/models/AccessTokenEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct AccessTokenEnvelope { 6 | public let accessToken: String 7 | public let user: User 8 | } 9 | 10 | extension AccessTokenEnvelope: Decodable { 11 | public static func decode(_ json: JSON) -> Decoded { 12 | return curry(AccessTokenEnvelope.init) 13 | <^> json <| "access_token" 14 | <*> json <| "user" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /KsApi/models/Activity.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Argo 3 | import Curry 4 | import Runes 5 | 6 | public struct Activity { 7 | public let category: Activity.Category 8 | public let comment: Comment? 9 | public let createdAt: TimeInterval 10 | public let id: Int 11 | public let memberData: MemberData 12 | public let project: Project? 13 | public let update: Update? 14 | public let user: User? 15 | 16 | public enum Category: String { 17 | case backing = "backing" 18 | case backingAmount = "backing-amount" 19 | case backingCanceled = "backing-canceled" 20 | case backingDropped = "backing-dropped" 21 | case backingReward = "backing-reward" 22 | case cancellation = "cancellation" 23 | case commentPost = "comment-post" 24 | case commentProject = "comment-project" 25 | case failure = "failure" 26 | case follow = "follow" 27 | case funding = "funding" 28 | case launch = "launch" 29 | case success = "success" 30 | case suspension = "suspension" 31 | case update = "update" 32 | case watch = "watch" 33 | case unknown = "unknown" 34 | } 35 | 36 | public struct MemberData { 37 | public let amount: Int? 38 | public let backing: Backing? 39 | public let oldAmount: Int? 40 | public let oldRewardId: Int? 41 | public let newAmount: Int? 42 | public let newRewardId: Int? 43 | public let rewardId: Int? 44 | } 45 | } 46 | 47 | extension Activity: Equatable { 48 | } 49 | public func == (lhs: Activity, rhs: Activity) -> Bool { 50 | return lhs.id == rhs.id 51 | } 52 | 53 | extension Activity: Decodable { 54 | public static func decode(_ json: JSON) -> Decoded { 55 | let create = curry(Activity.init) 56 | let tmp = create 57 | <^> json <| "category" 58 | <*> json <|? "comment" 59 | <*> json <| "created_at" 60 | <*> json <| "id" 61 | return tmp 62 | <*> Activity.MemberData.decode(json) 63 | <*> json <|? "project" 64 | <*> json <|? "update" 65 | <*> json <|? "user" 66 | } 67 | } 68 | 69 | extension Activity.Category: Decodable { 70 | public static func decode(_ json: JSON) -> Decoded { 71 | switch json { 72 | case let .string(category): 73 | return .success(Activity.Category(rawValue: category) ?? .unknown) 74 | default: 75 | return .failure(.typeMismatch(expected: "String", actual: json.description)) 76 | } 77 | } 78 | } 79 | 80 | extension Activity.MemberData: Decodable { 81 | public static func decode(_ json: JSON) -> Decoded { 82 | let create = curry(Activity.MemberData.init) 83 | let tmp = create 84 | <^> json <|? "amount" 85 | <*> json <|? "backing" 86 | <*> json <|? "old_amount" 87 | <*> json <|? "old_reward_id" 88 | return tmp 89 | <*> json <|? "new_amount" 90 | <*> json <|? "new_reward_id" 91 | <*> json <|? "reward_id" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /KsApi/models/ActivityEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ActivityEnvelope { 6 | public let activities: [Activity] 7 | public let urls: UrlsEnvelope 8 | 9 | public struct UrlsEnvelope { 10 | public let api: ApiEnvelope 11 | 12 | public struct ApiEnvelope { 13 | public let moreActivities: String 14 | } 15 | } 16 | } 17 | 18 | extension ActivityEnvelope: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | return curry(ActivityEnvelope.init) 21 | <^> json <|| "activities" 22 | <*> json <| "urls" 23 | } 24 | } 25 | 26 | extension ActivityEnvelope.UrlsEnvelope: Decodable { 27 | public static func decode(_ json: JSON) -> Decoded { 28 | return curry(ActivityEnvelope.UrlsEnvelope.init) 29 | <^> json <| "api" 30 | } 31 | } 32 | 33 | extension ActivityEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 34 | public static func decode(_ json: JSON) -> Decoded { 35 | return curry(ActivityEnvelope.UrlsEnvelope.ApiEnvelope.init) 36 | <^> (json <| "more_activities" <|> .success("")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/ActivityTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | 5 | final internal class ActivityTests: XCTestCase { 6 | 7 | func testEquatable() { 8 | XCTAssertEqual(Activity.template, Activity.template) 9 | } 10 | 11 | func testJSONDecoding_WithBadData() { 12 | let activity = Activity.decodeJSONDictionary([ 13 | "category": "update" 14 | ]) 15 | 16 | XCTAssertNotNil(activity.error) 17 | } 18 | 19 | func testJSONDecoding_WithGoodData() { 20 | let activity = Activity.decodeJSONDictionary([ 21 | "category": "update", 22 | "created_at": 123123123, 23 | "id": 1, 24 | ]) 25 | 26 | XCTAssertNil(activity.error) 27 | XCTAssertEqual(activity.value?.id, 1) 28 | } 29 | 30 | func testJSONParsing_WithMemberData() { 31 | let memberData = Activity.MemberData.decodeJSONDictionary([ 32 | "amount": 25.0, 33 | "backing": [ 34 | "amount": 1.0, 35 | "backer_id": 1, 36 | "id": 1, 37 | "location_id": 1, 38 | "pledged_at": 1000, 39 | "project_country": "US", 40 | "project_id": 1, 41 | "sequence": 1, 42 | "status": "pledged" 43 | ], 44 | "old_amount": 15.0, 45 | "old_reward_id": 1, 46 | "new_amount": 25.0, 47 | "new_reward_id": 2, 48 | "reward_id": 2 49 | ]) 50 | 51 | XCTAssertNil(memberData.error) 52 | XCTAssertEqual(25, memberData.value?.amount) 53 | XCTAssertEqual(1, memberData.value?.backing?.id) 54 | XCTAssertEqual(15, memberData.value?.oldAmount) 55 | XCTAssertEqual(1, memberData.value?.oldRewardId) 56 | XCTAssertEqual(25, memberData.value?.newAmount) 57 | XCTAssertEqual(2, memberData.value?.newRewardId) 58 | XCTAssertEqual(2, memberData.value?.rewardId) 59 | } 60 | 61 | func testJSONDecoding_WithNestedGoodData() { 62 | let activity = Activity.decodeJSONDictionary([ 63 | "category": "update", 64 | "created_at": 123123123, 65 | "id": 1, 66 | "user": [ 67 | "id": 2, 68 | "name": "User", 69 | "avatar": [ 70 | "medium": "img.jpg", 71 | "small": "img.jpg", 72 | "large": "img.jpg", 73 | ] 74 | ] 75 | ]) 76 | 77 | XCTAssertNil(activity.error) 78 | XCTAssertEqual(activity.value?.id, 1) 79 | XCTAssertEqual(activity.value?.user?.id, 2) 80 | } 81 | 82 | func testJSONDecoding_WithIncorrectCategory() { 83 | let activity = Activity.decodeJSONDictionary([ 84 | "category": "incorrect_category", 85 | "created_at": 123123123, 86 | "id": 1, 87 | ]) 88 | 89 | XCTAssertNil(activity.error) 90 | XCTAssertEqual(.some(.unknown), activity.value?.category) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /KsApi/models/Backing.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Backing { 6 | public let amount: Int 7 | public let backer: User? 8 | public let backerId: Int 9 | public let id: Int 10 | public let locationId: Int? 11 | public let pledgedAt: TimeInterval 12 | public let projectCountry: String 13 | public let projectId: Int 14 | public let reward: Reward? 15 | public let rewardId: Int? 16 | public let sequence: Int 17 | public let shippingAmount: Int? 18 | public let status: Status 19 | 20 | public enum Status: String { 21 | case canceled 22 | case collected 23 | case dropped 24 | case errored 25 | case pledged 26 | case preauth 27 | } 28 | } 29 | 30 | extension Backing: Equatable { 31 | } 32 | public func == (lhs: Backing, rhs: Backing) -> Bool { 33 | return lhs.id == rhs.id 34 | } 35 | 36 | extension Backing: Decodable { 37 | public static func decode(_ json: JSON) -> Decoded { 38 | let create = curry(Backing.init) 39 | let tmp1 = create 40 | <^> json <| "amount" 41 | <*> json <|? "backer" 42 | <*> json <| "backer_id" 43 | <*> json <| "id" 44 | let tmp2 = tmp1 45 | <*> json <|? "location_id" 46 | <*> json <| "pledged_at" 47 | <*> json <| "project_country" 48 | <*> json <| "project_id" 49 | return tmp2 50 | <*> json <|? "reward" 51 | <*> json <|? "reward_id" 52 | <*> json <| "sequence" 53 | <*> json <|? "shipping_amount" 54 | <*> json <| "status" 55 | } 56 | } 57 | 58 | extension Backing.Status: Decodable { 59 | } 60 | -------------------------------------------------------------------------------- /KsApi/models/BackingTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class BackingTests: XCTestCase { 5 | 6 | func testJSONDecoding_WithCompleteData() { 7 | let backing = Backing.decodeJSONDictionary([ 8 | "amount": 1.0, 9 | "backer_id": 1, 10 | "id": 1, 11 | "location_id": 1, 12 | "pledged_at": 1000, 13 | "project_country": "US", 14 | "project_id": 1, 15 | "sequence": 1, 16 | "status": "pledged" 17 | ]) 18 | 19 | XCTAssertNil(backing.error) 20 | XCTAssertEqual(1, backing.value?.id) 21 | XCTAssertEqual(Backing.Status.pledged, backing.value?.status) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /KsApi/models/CategoriesEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct CategoriesEnvelope { 6 | public let categories: [Category] 7 | } 8 | 9 | extension CategoriesEnvelope: Decodable { 10 | public static func decode(_ json: JSON) -> Decoded { 11 | return curry(CategoriesEnvelope.init) 12 | <^> json <|| "categories" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /KsApi/models/Category.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Category { 6 | public let color: Int? 7 | public let id: Int 8 | public let name: String 9 | // NB: To get around lack of recursive structs we package the parent category into an internal closure 10 | // and then expose a property that evaluates the closure. 11 | internal let _parent: () -> Category? 12 | public let parentId: Int? 13 | public let position: Int 14 | public let projectsCount: Int? 15 | public let slug: String 16 | 17 | public static let gamesId: Int = 12 18 | 19 | internal init(color: Int?, 20 | id: Int, 21 | name: String, 22 | parent: Category?, 23 | parentId: Int?, 24 | position: Int, 25 | projectsCount: Int?, 26 | slug: String) { 27 | self.color = color 28 | self.id = id 29 | self.name = name 30 | self._parent = { parent } 31 | self.parentId = parentId 32 | self.position = position 33 | self.projectsCount = projectsCount 34 | self.slug = slug 35 | } 36 | 37 | public var parent: Category? { 38 | return self._parent() 39 | } 40 | 41 | /// Returns the parent category if present, or returns self if we know for a fact that self is a 42 | /// root categeory. 43 | public var root: Category? { 44 | if let parent = self.parent { 45 | return parent 46 | } else if self.parentId == nil { 47 | return self 48 | } 49 | return nil 50 | } 51 | 52 | /// Returns the id of the root category. This is sometimes present in situations that `root` is not. 53 | public var rootId: Int? { 54 | return self.parentId ?? self.root?.id 55 | } 56 | 57 | public var isRoot: Bool { 58 | return self.parentId == nil && self.parent == nil 59 | } 60 | } 61 | 62 | extension Category: Equatable {} 63 | public func == (lhs: Category, rhs: Category) -> Bool { 64 | return lhs.id == rhs.id 65 | } 66 | 67 | extension Category: Hashable { 68 | public var hashValue: Int { 69 | return self.id 70 | } 71 | } 72 | 73 | extension Category: Comparable {} 74 | public func < (lhs: Category, rhs: Category) -> Bool { 75 | if lhs.id == rhs.id { 76 | return false 77 | } 78 | 79 | if lhs.isRoot && lhs.id == rhs.rootId { 80 | return true 81 | } 82 | 83 | if !lhs.isRoot && lhs.rootId == rhs.id { 84 | return false 85 | } 86 | 87 | if let lhsRootName = lhs.root?.name, let rhsRootName = rhs.root?.name { 88 | return lhsRootName < rhsRootName 89 | } 90 | 91 | return lhs.root == nil 92 | } 93 | 94 | extension Category: CustomStringConvertible, CustomDebugStringConvertible { 95 | public var description: String { 96 | return "Category(id: \(self.id), name: \(self.name))" 97 | } 98 | 99 | public var debugDescription: String { 100 | return self.description 101 | } 102 | } 103 | 104 | extension Category: Decodable { 105 | 106 | public static func decode(_ json: JSON) -> Decoded { 107 | let create = curry(Category.init) 108 | let tmp = create 109 | <^> json <|? "color" 110 | <*> json <| "id" 111 | <*> json <| "name" 112 | <*> json <|? "parent" 113 | return tmp 114 | <*> json <|? "parent_id" 115 | <*> json <| "position" 116 | <*> json <|? "projects_count" 117 | <*> json <| "slug" 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /KsApi/models/CategoryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | import Prelude 5 | 6 | class CategoryTests: XCTestCase { 7 | 8 | func testParent() { 9 | XCTAssertEqual(Category.illustration.parent, Category.art) 10 | XCTAssertEqual(Category.art.parent, nil) 11 | } 12 | 13 | func testRoot() { 14 | XCTAssertEqual(Category.illustration.root, Category.art) 15 | XCTAssertEqual(Category.illustration.isRoot, false) 16 | XCTAssertEqual(Category.art.root, Category.art) 17 | XCTAssertEqual(Category.art.isRoot, true) 18 | XCTAssertNil((Category.illustration |> Category.lens.parent .~ nil).root, 19 | "A subcategory with no parent category present does not have a root.") 20 | } 21 | 22 | func testEquatable() { 23 | XCTAssertEqual(Category.art, Category.art) 24 | XCTAssertNotEqual(Category.art, Category.illustration) 25 | } 26 | 27 | func testComparable() { 28 | let categories = [ 29 | Category.illustration, 30 | Category.documentary, 31 | Category.filmAndVideo, 32 | Category.art 33 | ] 34 | 35 | let sorted = [ 36 | Category.art, 37 | Category.illustration, 38 | Category.filmAndVideo, 39 | Category.documentary, 40 | ] 41 | 42 | XCTAssertEqual(sorted, categories.sorted()) 43 | } 44 | 45 | func testDescription() { 46 | XCTAssertNotEqual(Category.art.description, "") 47 | XCTAssertNotEqual(Category.art.debugDescription, "") 48 | } 49 | 50 | func testJSONParsing_WithPartialData() { 51 | let c1 = Category.decodeJSONDictionary([ 52 | "id": 1 53 | ]) 54 | XCTAssertNotNil(c1.error) 55 | 56 | let c2 = Category.decodeJSONDictionary([ 57 | "id": 1, 58 | "name": "Art" 59 | ]) 60 | XCTAssertNotNil(c2.error) 61 | 62 | let c3 = Category.decodeJSONDictionary([ 63 | "id": 1, 64 | "name": "Art", 65 | "slug": "art" 66 | ]) 67 | XCTAssertNotNil(c3.error) 68 | } 69 | 70 | func testJSONParsing_WithFullData() { 71 | 72 | let c4 = Category.decodeJSONDictionary([ 73 | "id": 1, 74 | "name": "Art", 75 | "slug": "art", 76 | "position": 1 77 | ]) 78 | XCTAssertNil(c4.error) 79 | XCTAssertEqual(c4.value?.id, 1) 80 | XCTAssertEqual(c4.value?.name, "Art") 81 | XCTAssertEqual(c4.value?.slug, "art") 82 | XCTAssertEqual(c4.value?.position, 1) 83 | 84 | let c5 = Category.decodeJSONDictionary([ 85 | "id": 22, 86 | "name": "Illustration", 87 | "slug": "art/illustration", 88 | "position": 4, 89 | "projects_count": 44, 90 | "parent": [ 91 | "id": 1, 92 | "name": "Art", 93 | "slug": "art", 94 | "position": 1 95 | ] 96 | ]) 97 | XCTAssertNil(c5.error) 98 | XCTAssertEqual(c5.value?.id, 22) 99 | XCTAssertEqual(c5.value?.name, "Illustration") 100 | XCTAssertNotEqual(c5.value?.parent, nil) 101 | XCTAssertEqual(c5.value?.parent?.name, "Art") 102 | XCTAssertEqual(c5.value?.isRoot, false) 103 | XCTAssertEqual(c5.value?.root, c5.value?.parent) 104 | XCTAssertEqual(1, c5.value?.rootId) 105 | XCTAssertEqual(c5.value?.projectsCount, 44) 106 | } 107 | 108 | func testJSONParsing_WithPartialParentData() { 109 | 110 | let c6 = Category.decodeJSONDictionary([ 111 | "id": 22, 112 | "name": "Illustration", 113 | "slug": "art/illustration", 114 | "position": 4 115 | ]) 116 | XCTAssertNil(c6.error) 117 | XCTAssertEqual(c6.value?.id, 22) 118 | XCTAssertEqual(c6.value?.name, "Illustration") 119 | XCTAssertEqual(c6.value?.parent, nil) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /KsApi/models/ChangePaymentMethodEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ChangePaymentMethodEnvelope { 6 | public let newCheckoutUrl: String? 7 | public let status: Int 8 | } 9 | 10 | extension ChangePaymentMethodEnvelope: Decodable { 11 | public static func decode(_ json: JSON) -> Decoded { 12 | return curry(ChangePaymentMethodEnvelope.init) 13 | <^> json <|? ["data", "new_checkout_url"] 14 | <*> ((json <| "status" >>- stringToIntOrZero) <|> (json <| "status")) 15 | } 16 | } 17 | 18 | private func stringToIntOrZero(_ string: String) -> Decoded { 19 | return 20 | Double(string).flatMap(Int.init).map(Decoded.success) 21 | ?? Int(string).map(Decoded.success) 22 | ?? .success(0) 23 | } 24 | -------------------------------------------------------------------------------- /KsApi/models/ChangePaymentMethodEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class ChangePaymentMethodEnvelopeTests: XCTestCase { 5 | 6 | func testDecodingWithStringStatus() { 7 | let decoded = ChangePaymentMethodEnvelope.decodeJSONDictionary(["status": "200"]) 8 | XCTAssertNil(decoded.error) 9 | XCTAssertEqual(200, decoded.value?.status) 10 | } 11 | 12 | func testDecodingWithIntStatus() { 13 | let decoded = ChangePaymentMethodEnvelope.decodeJSONDictionary(["status": 200]) 14 | XCTAssertNil(decoded.error) 15 | XCTAssertEqual(200, decoded.value?.status) 16 | } 17 | 18 | func testDecodingWithMissingStatus() { 19 | let decoded = ChangePaymentMethodEnvelope.decodeJSONDictionary([:]) 20 | XCTAssertNotNil(decoded.error) 21 | } 22 | 23 | func testDecodingWithBadStatusData() { 24 | let decoded = ChangePaymentMethodEnvelope.decodeJSONDictionary(["status": "bad data"]) 25 | XCTAssertNil(decoded.error) 26 | XCTAssertEqual(0, decoded.value?.status) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /KsApi/models/CheckoutEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct CheckoutEnvelope { 6 | public enum State: String { 7 | case authorizing 8 | case failed 9 | case successful 10 | case verifying 11 | } 12 | public let state: State 13 | public let stateReason: String 14 | } 15 | 16 | extension CheckoutEnvelope: Decodable { 17 | public static func decode(_ json: JSON) -> Decoded { 18 | let create = curry(CheckoutEnvelope.init) 19 | return create 20 | <^> json <| "state" 21 | <*> (json <| "state_reason" <|> .success("")) 22 | } 23 | } 24 | 25 | extension CheckoutEnvelope.State: Decodable { 26 | } 27 | -------------------------------------------------------------------------------- /KsApi/models/CheckoutEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | @testable import Argo 4 | 5 | final class CheckoutEnvelopeTests: XCTestCase { 6 | func testJsonDecoding() { 7 | let json: [String:Any] = [ 8 | "state": "failed", 9 | "state_reason": "Oof!" 10 | ] 11 | 12 | let envelope = CheckoutEnvelope.decodeJSONDictionary(json) 13 | 14 | XCTAssertEqual(CheckoutEnvelope.State.failed, envelope.value?.state) 15 | XCTAssertEqual("Oof!", envelope.value?.stateReason) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /KsApi/models/Comment.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Comment { 6 | public let author: User 7 | public let body: String 8 | public let createdAt: TimeInterval 9 | public let deletedAt: TimeInterval? 10 | public let id: Int 11 | } 12 | 13 | extension Comment: Decodable { 14 | public static func decode(_ json: JSON) -> Decoded { 15 | let create = curry(Comment.init) 16 | let tmp = create 17 | <^> json <| "author" 18 | <*> json <| "body" 19 | <*> json <| "created_at" 20 | return tmp 21 | <*> (json <|? "deleted_at" >>- decodePositiveTimeInterval) 22 | <*> json <| "id" 23 | } 24 | } 25 | 26 | extension Comment: Equatable { 27 | } 28 | public func == (lhs: Comment, rhs: Comment) -> Bool { 29 | return lhs.id == rhs.id 30 | } 31 | 32 | // Decode a time interval so that non-positive values are coalesced to `nil`. We do this because the API 33 | // sends back `0` when the comment hasn't been deleted, and we'd rather handle that value as `nil`. 34 | private func decodePositiveTimeInterval(_ interval: TimeInterval?) -> Decoded { 35 | if let interval = interval, interval > 0.0 { 36 | return .success(interval) 37 | } 38 | return .success(nil) 39 | } 40 | -------------------------------------------------------------------------------- /KsApi/models/CommentTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class CommentTests: XCTestCase { 5 | 6 | func testJSONParsing_WithCompleteData() { 7 | 8 | let comment = Comment.decodeJSONDictionary([ 9 | "author": [ 10 | "id": 1, 11 | "name": "Blob", 12 | "avatar": [ 13 | "medium": "http://www.kickstarter.com/medium.jpg", 14 | "small": "http://www.kickstarter.com/small.jpg" 15 | ] 16 | ], 17 | "body": "hello!", 18 | "created_at": 123456789.0, 19 | "deleted_at": 123456789.0, 20 | "id": 1 21 | ]) 22 | 23 | XCTAssertNil(comment.error) 24 | XCTAssertEqual(1, comment.value?.id) 25 | } 26 | 27 | func testJSONParsing_ZeroDeletedAt() { 28 | 29 | let comment = Comment.decodeJSONDictionary([ 30 | "author": [ 31 | "id": 1, 32 | "name": "Blob", 33 | "avatar": [ 34 | "medium": "http://www.kickstarter.com/medium.jpg", 35 | "small": "http://www.kickstarter.com/small.jpg" 36 | ] 37 | ], 38 | "body": "hello!", 39 | "created_at": 123456789.0, 40 | "deleted_at": 0, 41 | "id": 1 42 | ]) 43 | 44 | XCTAssertNil(comment.error) 45 | XCTAssertNotNil(comment.value) 46 | XCTAssertNil(comment.value?.deletedAt) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /KsApi/models/CommentsEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct CommentsEnvelope { 6 | public let comments: [Comment] 7 | public let urls: UrlsEnvelope 8 | 9 | public struct UrlsEnvelope { 10 | public let api: ApiEnvelope 11 | 12 | public struct ApiEnvelope { 13 | public let moreComments: String 14 | } 15 | } 16 | } 17 | 18 | extension CommentsEnvelope: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | return curry(CommentsEnvelope.init) 21 | <^> json <|| "comments" 22 | <*> json <| "urls" 23 | } 24 | } 25 | 26 | extension CommentsEnvelope.UrlsEnvelope: Decodable { 27 | public static func decode(_ json: JSON) -> Decoded { 28 | return curry(CommentsEnvelope.UrlsEnvelope.init) 29 | <^> json <| "api" 30 | } 31 | } 32 | 33 | extension CommentsEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 34 | public static func decode(_ json: JSON) -> Decoded { 35 | return curry(CommentsEnvelope.UrlsEnvelope.ApiEnvelope.init) 36 | <^> (json <| "more_comments" <|> .success("")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/Config.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Config { 6 | public let abExperiments: [String:String] 7 | public let appId: Int 8 | public let applePayCountries: [String] 9 | public let countryCode: String 10 | public let features: [String:Bool] 11 | public let iTunesLink: String 12 | public let launchedCountries: [Project.Country] 13 | public let locale: String 14 | public let stripePublishableKey: String 15 | } 16 | 17 | extension Config: Decodable { 18 | public static func decode(_ json: JSON) -> Decoded { 19 | let create = curry(Config.init) 20 | let tmp = create 21 | <^> decodeDictionary(json <| "ab_experiments") 22 | <*> json <| "app_id" 23 | <*> json <|| "apple_pay_countries" 24 | <*> json <| "country_code" 25 | <*> decodeDictionary(json <| "features") 26 | return tmp 27 | <*> json <| "itunes_link" 28 | <*> json <|| "launched_countries" 29 | <*> json <| "locale" 30 | <*> json <| ["stripe", "publishable_key"] 31 | } 32 | } 33 | 34 | extension Config: Equatable { 35 | } 36 | public func == (lhs: Config, rhs: Config) -> Bool { 37 | return lhs.abExperiments == rhs.abExperiments && 38 | lhs.appId == rhs.appId && 39 | lhs.applePayCountries == rhs.applePayCountries && 40 | lhs.countryCode == rhs.countryCode && 41 | lhs.features == rhs.features && 42 | lhs.iTunesLink == rhs.iTunesLink && 43 | lhs.launchedCountries == rhs.launchedCountries && 44 | lhs.locale == rhs.locale && 45 | lhs.stripePublishableKey == rhs.stripePublishableKey 46 | } 47 | 48 | extension Config: EncodableType { 49 | public func encode() -> [String:Any] { 50 | var result: [String:Any] = [:] 51 | result["ab_experiments"] = self.abExperiments 52 | result["app_id"] = self.appId 53 | result["apple_pay_countries"] = self.applePayCountries 54 | result["country_code"] = self.countryCode 55 | result["features"] = self.features 56 | result["itunes_link"] = self.iTunesLink 57 | result["launched_countries"] = self.launchedCountries.map { $0.encode() } 58 | result["locale"] = self.locale 59 | result["stripe"] = ["publishable_key": self.stripePublishableKey] 60 | return result 61 | } 62 | } 63 | 64 | // Useful for getting around swift optimization bug: https://github.com/thoughtbot/Argo/issues/363 65 | // Turns out using `>>-` or `flatMap` on a `Decoded` fails to compile with optimizations on, so this 66 | // function does it manually. 67 | private func decodeDictionary(_ j: Decoded) 68 | -> Decoded<[String:T]> where T.DecodedType == T { 69 | switch j { 70 | case let .success(json): return [String: T].decode(json) 71 | case let .failure(e): return .failure(e) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /KsApi/models/ConfigTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | import Curry 5 | import Runes 6 | 7 | final class ConfigTests: XCTestCase { 8 | 9 | func testDecoding() { 10 | let abExperiments = [ 11 | "2001_space_odyssey": "control", 12 | "dr_strangelove": "experiment" 13 | ] 14 | let features = [ 15 | "feature1": true, 16 | "feature2": false, 17 | ] 18 | let json: [String:Any] = [ 19 | "ab_experiments": abExperiments, 20 | "app_id": 123456789, 21 | "apple_pay_countries": ["US", "GB", "CA"], 22 | "country_code": "US", 23 | "features": features, 24 | "itunes_link": "http://www.itunes.com", 25 | "launched_countries": [ 26 | [ "trailing_code": false, 27 | "currency_symbol": "€", 28 | "currency_code": "EUR", 29 | "name": "ES" ], 30 | [ 31 | "trailing_code": false, 32 | "currency_symbol": "€", 33 | "currency_code": "EUR", 34 | "name": "FR" ] 35 | ], 36 | "locale": "en", 37 | "stripe": [ 38 | "publishable_key": "pk" 39 | ] 40 | ] 41 | 42 | // Confirm json decoded successfully 43 | let decodedConfig = Config.decodeJSONDictionary(json) 44 | XCTAssertNil(decodedConfig.error) 45 | 46 | // Confirm fields decoded properly 47 | let config = decodedConfig.value! 48 | XCTAssertEqual(abExperiments, config.abExperiments) 49 | XCTAssertEqual(123456789, config.appId) 50 | XCTAssertEqual("US", config.countryCode) 51 | XCTAssertEqual(["US", "GB", "CA"], config.applePayCountries) 52 | XCTAssertEqual(features, config.features) 53 | XCTAssertEqual("http://www.itunes.com", config.iTunesLink) 54 | XCTAssertEqual([.ES, .FR], config.launchedCountries) 55 | XCTAssertEqual("en", config.locale) 56 | XCTAssertEqual("pk", config.stripePublishableKey) 57 | 58 | // Confirm that encoding and decoding again results in the same config. 59 | XCTAssertEqual(config, Config.decodeJSONDictionary(config.encode()).value) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /KsApi/models/CreatePledgeEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct CreatePledgeEnvelope { 6 | public let checkoutUrl: String? 7 | public let newCheckoutUrl: String? 8 | public let status: Int 9 | } 10 | 11 | extension CreatePledgeEnvelope: Decodable { 12 | public static func decode(_ json: JSON) -> Decoded { 13 | return curry(CreatePledgeEnvelope.init) 14 | <^> json <|? ["data", "checkout_url"] 15 | <*> json <|? ["data", "new_checkout_url"] 16 | <*> ((json <| "status" >>- stringToIntOrZero) <|> (json <| "status")) 17 | } 18 | } 19 | 20 | private func stringToIntOrZero(_ string: String) -> Decoded { 21 | return 22 | Double(string).flatMap(Int.init).map(Decoded.success) 23 | ?? Int(string).map(Decoded.success) 24 | ?? .success(0) 25 | } 26 | -------------------------------------------------------------------------------- /KsApi/models/CreatePledgeEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class CreatePledgeEnvelopeTests: XCTestCase { 5 | 6 | func testDecodingWithStringStatus() { 7 | let decoded = CreatePledgeEnvelope.decodeJSONDictionary(["status": "200"]) 8 | XCTAssertNil(decoded.error) 9 | XCTAssertEqual(200, decoded.value?.status) 10 | } 11 | 12 | func testDecodingWithIntStatus() { 13 | let decoded = CreatePledgeEnvelope.decodeJSONDictionary(["status": 200]) 14 | XCTAssertNil(decoded.error) 15 | XCTAssertEqual(200, decoded.value?.status) 16 | } 17 | 18 | func testDecodingWithMissingStatus() { 19 | let decoded = CreatePledgeEnvelope.decodeJSONDictionary([:]) 20 | XCTAssertNotNil(decoded.error) 21 | } 22 | 23 | func testDecodingWithBadStatusData() { 24 | let decoded = CreatePledgeEnvelope.decodeJSONDictionary(["status": "bad data"]) 25 | XCTAssertNil(decoded.error) 26 | XCTAssertEqual(0, decoded.value?.status) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /KsApi/models/DiscoveryEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct DiscoveryEnvelope { 6 | public let projects: [Project] 7 | public let urls: UrlsEnvelope 8 | public let stats: StatsEnvelope 9 | 10 | public struct UrlsEnvelope { 11 | public let api: ApiEnvelope 12 | 13 | public struct ApiEnvelope { 14 | public let moreProjects: String 15 | 16 | public init(more_projects: String) { 17 | moreProjects = more_projects 18 | } 19 | } 20 | } 21 | 22 | public struct StatsEnvelope { 23 | public let count: Int 24 | } 25 | } 26 | 27 | extension DiscoveryEnvelope: Decodable { 28 | public static func decode(_ json: JSON) -> Decoded { 29 | return curry(DiscoveryEnvelope.init) 30 | <^> json <|| "projects" 31 | <*> json <| "urls" 32 | <*> json <| "stats" 33 | } 34 | } 35 | 36 | extension DiscoveryEnvelope.UrlsEnvelope: Decodable { 37 | public static func decode(_ json: JSON) -> Decoded { 38 | return curry(DiscoveryEnvelope.UrlsEnvelope.init) 39 | <^> json <| "api" 40 | } 41 | } 42 | 43 | extension DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 44 | public static func decode(_ json: JSON) -> Decoded { 45 | return curry(DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope.init) 46 | <^> json <| "more_projects" 47 | } 48 | } 49 | 50 | extension DiscoveryEnvelope.StatsEnvelope: Decodable { 51 | public static func decode(_ json: JSON) -> Decoded { 52 | return curry(DiscoveryEnvelope.StatsEnvelope.init) 53 | <^> json <| "count" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /KsApi/models/ErrorEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | @testable import Argo 4 | 5 | class ErrorEnvelopeTests: XCTestCase { 6 | 7 | func testJsonDecodingWithFullData() { 8 | let env = ErrorEnvelope.decodeJSONDictionary([ 9 | "error_messages": ["hello"], 10 | "ksr_code": "access_token_invalid", 11 | "http_code": 401, 12 | "exception": [ 13 | "backtrace": ["hello"], 14 | "message": "hello" 15 | ] 16 | ]) 17 | XCTAssertNotNil(env) 18 | } 19 | 20 | func testJsonDecodingWithBadKsrCode() { 21 | let env = ErrorEnvelope.decodeJSONDictionary([ 22 | "error_messages": ["hello"], 23 | "ksr_code": "doesnt_exist", 24 | "http_code": 401, 25 | "exception": [ 26 | "backtrace": ["hello"], 27 | "message": "hello" 28 | ] 29 | ]) 30 | XCTAssertNil(env.error) 31 | XCTAssertEqual(ErrorEnvelope.KsrCode.UnknownCode, env.value?.ksrCode) 32 | } 33 | 34 | func testJsonDecodingWithNonStandardError() { 35 | let env = ErrorEnvelope.decodeJSONDictionary([ 36 | "status": 406, 37 | "data": [ 38 | "errors": [ 39 | "amount": [ 40 | "Bad amount" 41 | ] 42 | ] 43 | ] 44 | ]) 45 | XCTAssertNil(env.error) 46 | XCTAssertEqual(ErrorEnvelope.KsrCode.UnknownCode, env.value?.ksrCode) 47 | XCTAssertEqual(["Bad amount"], env.value!.errorMessages) 48 | XCTAssertEqual(406, env.value?.httpCode) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /KsApi/models/FindFriendsEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct FindFriendsEnvelope { 6 | public let contactsImported: Bool 7 | public let urls: UrlsEnvelope 8 | public let users: [User] 9 | 10 | public struct UrlsEnvelope { 11 | public let api: ApiEnvelope 12 | 13 | public struct ApiEnvelope { 14 | public let moreUsers: String? 15 | } 16 | } 17 | } 18 | 19 | extension FindFriendsEnvelope: Decodable { 20 | public static func decode(_ json: JSON) -> Decoded { 21 | return curry(FindFriendsEnvelope.init) 22 | <^> json <| "contacts_imported" 23 | <*> json <| "urls" 24 | <*> (json <|| "users" <|> .success([])) 25 | } 26 | } 27 | 28 | extension FindFriendsEnvelope.UrlsEnvelope: Decodable { 29 | public static func decode(_ json: JSON) -> Decoded { 30 | return curry(FindFriendsEnvelope.UrlsEnvelope.init) 31 | <^> json <| "api" 32 | } 33 | } 34 | 35 | extension FindFriendsEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 36 | public static func decode(_ json: JSON) -> Decoded { 37 | return curry(FindFriendsEnvelope.UrlsEnvelope.ApiEnvelope.init) 38 | <^> json <|? "more_users" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /KsApi/models/FindFriendsEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class FindFriendsEnvelopeTests: XCTestCase { 5 | func testJsonDecoding() { 6 | let json: [String:Any] = [ 7 | "contacts_imported": true, 8 | "urls": [ 9 | "api": [ 10 | "more_users": "http://api.dev/v1/users/self/friends/find?count=10" 11 | ] 12 | ], 13 | "users": [ 14 | [ 15 | "id": 1, 16 | "name": "Blob", 17 | "avatar": [ 18 | "medium": "http://www.kickstarter.com/medium.jpg", 19 | "small": "http://www.kickstarter.com/small.jpg" 20 | ], 21 | "backed_projects_count": 2, 22 | "weekly_newsletter": false, 23 | "promo_newsletter": false, 24 | "happening_newsletter": false, 25 | "games_newsletter": false, 26 | "facebook_connected": false, 27 | "location": [ 28 | "id": 12, 29 | "displayable_name": "Brooklyn, NY", 30 | "name": "Brooklyn" 31 | ], 32 | "is_friend": false 33 | ], 34 | [ 35 | "id": 2, 36 | "name": "Blab", 37 | "avatar": [ 38 | "medium": "http://www.kickstarter.com/medium.jpg", 39 | "small": "http://www.kickstarter.com/small.jpg" 40 | ], 41 | "backed_projects_count": 2, 42 | "weekly_newsletter": false, 43 | "promo_newsletter": false, 44 | "happening_newsletter": false, 45 | "games_newsletter": false, 46 | "facebook_connected": false, 47 | "location": [ 48 | "id": 12, 49 | "displayable_name": "Brooklyn, NY", 50 | "name": "Brooklyn" 51 | ], 52 | "is_friend": true 53 | ] 54 | ] 55 | ] 56 | 57 | let friends = FindFriendsEnvelope.decodeJSONDictionary(json) 58 | let users = friends.value?.users ?? [] 59 | 60 | XCTAssertEqual(true, friends.value?.contactsImported) 61 | XCTAssertEqual("http://api.dev/v1/users/self/friends/find?count=10", 62 | friends.value?.urls.api.moreUsers) 63 | XCTAssertEqual(false, users[0].isFriend) 64 | XCTAssertEqual(true, users[1].isFriend) 65 | } 66 | // swiftlint:enable function_body_length 67 | 68 | func testJsonDecoding_MissingData() { 69 | let json: [String:Any] = [ 70 | "contacts_imported": true, 71 | "urls": [ 72 | "api": [ 73 | ] 74 | ], 75 | "users": [ 76 | ] 77 | ] 78 | 79 | let friends = FindFriendsEnvelope.decodeJSONDictionary(json) 80 | 81 | XCTAssertNil(friends.value?.urls.api.moreUsers) 82 | XCTAssertEqual([], friends.value?.users ?? []) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /KsApi/models/FriendStatsEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct FriendStatsEnvelope { 6 | public let stats: Stats 7 | 8 | public struct Stats { 9 | public let friendProjectsCount: Int 10 | public let remoteFriendsCount: Int 11 | } 12 | } 13 | 14 | extension FriendStatsEnvelope: Decodable { 15 | public static func decode(_ json: JSON) -> Decoded { 16 | return curry(FriendStatsEnvelope.init) 17 | <^> json <| "stats" 18 | } 19 | } 20 | 21 | extension FriendStatsEnvelope.Stats: Decodable { 22 | public static func decode(_ json: JSON) -> Decoded { 23 | return curry(FriendStatsEnvelope.Stats.init) 24 | <^> json <| "friend_projects_count" 25 | <*> json <| "remote_friends_count" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /KsApi/models/FriendStatsEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | @testable import Argo 4 | 5 | final class FriendStatsEnvelopeTests: XCTestCase { 6 | func testJsonDecoding() { 7 | let json: [String:Any] = [ 8 | "stats": [ 9 | "remote_friends_count": 202, 10 | "friend_projects_count": 1132 11 | ] 12 | ] 13 | 14 | let stats = FriendStatsEnvelope.decodeJSONDictionary(json) 15 | 16 | XCTAssertEqual(202, stats.value?.stats.remoteFriendsCount) 17 | XCTAssertEqual(1132, stats.value?.stats.friendProjectsCount) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /KsApi/models/Item.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Item { 6 | public let description: String? 7 | public let id: Int 8 | public let name: String 9 | public let projectId: Int 10 | } 11 | 12 | extension Item: Decodable { 13 | public static func decode(_ json: JSON) -> Decoded { 14 | let create = curry(Item.init) 15 | return create 16 | <^> json <|? "description" 17 | <*> json <| "id" 18 | <*> json <| "name" 19 | <*> json <| "project_id" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /KsApi/models/ItemTests.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | import XCTest 3 | @testable import KsApi 4 | 5 | final class ItemTests: XCTestCase { 6 | 7 | func testDecoding() { 8 | let decoded = Item.decodeJSONDictionary([ 9 | "description": "Hello", 10 | "id": 1, 11 | "name": "The thing", 12 | "project_id": 1 13 | ]) 14 | 15 | XCTAssertNil(decoded.error) 16 | XCTAssertEqual("Hello", decoded.value?.description) 17 | XCTAssertEqual(1, decoded.value?.id) 18 | XCTAssertEqual("The thing", decoded.value?.name) 19 | XCTAssertEqual(1, decoded.value?.projectId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /KsApi/models/Location.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct Location { 6 | public let country: String 7 | public let displayableName: String 8 | public let id: Int 9 | public let name: String 10 | 11 | public static let none = Location(country: "", displayableName: "", id: -42, name: "") 12 | } 13 | 14 | extension Location: Equatable {} 15 | public func == (lhs: Location, rhs: Location) -> Bool { 16 | return lhs.id == rhs.id 17 | } 18 | 19 | extension Location: Decodable { 20 | static public func decode(_ json: JSON) -> Decoded { 21 | return curry(Location.init) 22 | <^> json <| "country" 23 | <*> json <| "displayable_name" 24 | <*> json <| "id" 25 | <*> json <| "name" 26 | } 27 | } 28 | 29 | extension Location: EncodableType { 30 | public func encode() -> [String:Any] { 31 | var result: [String:Any] = [:] 32 | result["country"] = self.country 33 | result["displayable_name"] = self.displayableName 34 | result["id"] = self.id 35 | result["name"] = self.name 36 | return result 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/LocationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Prelude 4 | 5 | final class LocationTests: XCTestCase { 6 | 7 | func testEquatable() { 8 | XCTAssertEqual(Location.template, Location.template) 9 | XCTAssertNotEqual(Location.template, Location.template |> Location.lens.id %~ { $0 + 1 }) 10 | } 11 | 12 | func testJSONParsing_WithPartialData() { 13 | 14 | let location = Location.decodeJSONDictionary([ 15 | "id": 1 16 | ]) 17 | 18 | XCTAssertNotNil(location.error) 19 | } 20 | 21 | func testJSONParsing_WithFullData() { 22 | 23 | let location = Location.decodeJSONDictionary([ 24 | "country": "US", 25 | "id": 1, 26 | "displayable_name": "Brooklyn, NY", 27 | "name": "Brooklyn" 28 | ]) 29 | 30 | XCTAssertNil(location.error) 31 | XCTAssertEqual(location.value?.id, 1) 32 | XCTAssertEqual(location.value?.displayableName, "Brooklyn, NY") 33 | XCTAssertEqual(location.value?.name, "Brooklyn") 34 | } 35 | 36 | func testEncodeDecode() { 37 | let location: [String:Any] = [ 38 | "country": "US", 39 | "id": 44, 40 | "displayable_name": "New Amsterdam, NY", 41 | "name": "New Amsterdam" 42 | ] 43 | 44 | let decodedLocation = Location.decodeJSONDictionary(location).value 45 | 46 | XCTAssertEqual(decodedLocation, Location.decodeJSONDictionary(decodedLocation?.encode() ?? [:]).value) 47 | XCTAssertEqual(decodedLocation?.encode() as NSDictionary?, location as NSDictionary?) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /KsApi/models/Message.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | import Foundation 5 | 6 | public struct Message { 7 | public let body: String 8 | public let createdAt: TimeInterval 9 | public let id: Int 10 | public let recipient: User 11 | public let sender: User 12 | } 13 | 14 | extension Message: Decodable { 15 | public static func decode(_ json: JSON) -> Decoded { 16 | let create = curry(Message.init) 17 | return create 18 | <^> json <| "body" 19 | <*> json <| "created_at" 20 | <*> json <| "id" 21 | <*> json <| "recipient" 22 | <*> json <| "sender" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /KsApi/models/MessageSubject.swift: -------------------------------------------------------------------------------- 1 | public enum MessageSubject { 2 | case backing(Backing) 3 | case messageThread(MessageThread) 4 | case project(Project) 5 | 6 | public var backing: Backing? { 7 | if case let .backing(backing) = self { 8 | return backing 9 | } 10 | return nil 11 | } 12 | 13 | public var messageThread: MessageThread? { 14 | if case let .messageThread(messageThread) = self { 15 | return messageThread 16 | } 17 | return nil 18 | } 19 | 20 | public var project: Project? { 21 | if case let .project(project) = self { 22 | return project 23 | } 24 | return nil 25 | } 26 | } 27 | 28 | extension MessageSubject: Equatable {} 29 | public func == (lhs: MessageSubject, rhs: MessageSubject) -> Bool { 30 | switch (lhs, rhs) { 31 | case let (.backing(lhs), .backing(rhs)): 32 | return lhs == rhs 33 | case let (.messageThread(lhs), .messageThread(rhs)): 34 | return lhs == rhs 35 | case let (.project(lhs), .project(rhs)): 36 | return lhs == rhs 37 | default: 38 | return false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /KsApi/models/MessageTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | 5 | internal final class MessageTests: XCTestCase { 6 | func testDecoding() { 7 | let result = Message.decodeJSONDictionary([ 8 | "body": "Hello!", 9 | "created_at": 123456789.0, 10 | "id": 1, 11 | "recipient": [ 12 | "id": 1, 13 | "name": "Blob", 14 | "avatar": [ 15 | "medium": "img", 16 | "small": "img" 17 | ], 18 | ], 19 | "sender": [ 20 | "id": 2, 21 | "name": "Clob", 22 | "avatar": [ 23 | "medium": "img", 24 | "small": "img" 25 | ], 26 | ] 27 | ]) 28 | 29 | XCTAssertNil(result.error) 30 | XCTAssertNotNil(result.value) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /KsApi/models/MessageThread.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct MessageThread { 6 | public let backing: Backing? 7 | public let closed: Bool 8 | public let id: Int 9 | public let lastMessage: Message 10 | public let participant: User 11 | public let project: Project 12 | public let unreadMessagesCount: Int 13 | } 14 | 15 | extension MessageThread: Decodable { 16 | public static func decode(_ json: JSON) -> Decoded { 17 | let create = curry(MessageThread.init) 18 | let tmp = create 19 | <^> json <|? "backing" 20 | <*> json <| "closed" 21 | <*> json <| "id" 22 | <*> json <| "last_message" 23 | return tmp 24 | <*> json <| "participant" 25 | <*> json <| "project" 26 | <*> json <| "unread_messages_count" 27 | } 28 | } 29 | 30 | extension MessageThread: Equatable {} 31 | public func == (lhs: MessageThread, rhs: MessageThread) -> Bool { 32 | return lhs.id == rhs.id 33 | } 34 | -------------------------------------------------------------------------------- /KsApi/models/MessageThreadEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct MessageThreadEnvelope { 6 | public let participants: [User] 7 | public let messages: [Message] 8 | public let messageThread: MessageThread 9 | } 10 | 11 | extension MessageThreadEnvelope: Decodable { 12 | public static func decode(_ json: JSON) -> Decoded { 13 | return curry(MessageThreadEnvelope.init) 14 | <^> json <|| "participants" 15 | <*> json <|| "messages" 16 | <*> json <| "message_thread" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /KsApi/models/MessageThreadTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | 5 | internal final class MessageThreadTests: XCTestCase { 6 | func testDecoding() { 7 | let result = MessageThread.decodeJSONDictionary([ 8 | "closed": false, 9 | "id": 1, 10 | "last_message": [ 11 | "body": "Hello!", 12 | "created_at": 123456789.0, 13 | "id": 1, 14 | "recipient": [ 15 | "id": 1, 16 | "name": "Blob", 17 | "avatar": [ 18 | "medium": "img", 19 | "small": "img" 20 | ], 21 | ], 22 | "sender": [ 23 | "id": 2, 24 | "name": "Clob", 25 | "avatar": [ 26 | "medium": "img", 27 | "small": "img" 28 | ], 29 | ] 30 | ], 31 | "unread_messages_count": 1, 32 | "participant": [ 33 | "id": 1, 34 | "name": "Blob", 35 | "avatar": [ 36 | "medium": "img", 37 | "small": "img" 38 | ], 39 | ], 40 | "project": [ 41 | "id": 1, 42 | "name": "Project", 43 | "blurb": "The project blurb", 44 | "pledged": 1_000, 45 | "goal": 2_000, 46 | "category": [ 47 | "id": 1, 48 | "name": "Art", 49 | "slug": "art", 50 | "position": 1 51 | ], 52 | "creator": [ 53 | "id": 1, 54 | "name": "Blob", 55 | "avatar": [ 56 | "medium": "http://www.kickstarter.com/medium.jpg", 57 | "small": "http://www.kickstarter.com/small.jpg" 58 | ] 59 | ], 60 | "photo": [ 61 | "full": "http://www.kickstarter.com/full.jpg", 62 | "med": "http://www.kickstarter.com/med.jpg", 63 | "small": "http://www.kickstarter.com/small.jpg", 64 | "1024x768": "http://www.kickstarter.com/1024x768.jpg", 65 | ], 66 | "location": [ 67 | "country": "US", 68 | "id": 1, 69 | "displayable_name": "Brooklyn, NY", 70 | "name": "Brooklyn" 71 | ], 72 | "video": [ 73 | "id": 1, 74 | "high": "kickstarter.com/video.mp4" 75 | ], 76 | "backers_count": 10, 77 | "currency_symbol": "$", 78 | "currency": "USD", 79 | "currency_trailing_code": false, 80 | "country": "US", 81 | "launched_at": 1000, 82 | "deadline": 1000, 83 | "state_changed_at": 1000, 84 | "static_usd_rate": 1.0, 85 | "slug": "project", 86 | "urls": [ 87 | "web": [ 88 | "project": "https://www.kickstarter.com/projects/my-cool-projects" 89 | ] 90 | ], 91 | "state": "live" 92 | ] 93 | ]) 94 | 95 | XCTAssertNil(result.error) 96 | XCTAssertNotNil(result.value) 97 | } 98 | } 99 | // swiftlint:enable function_body_length 100 | -------------------------------------------------------------------------------- /KsApi/models/MessageThreadsEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct MessageThreadsEnvelope { 6 | public let messageThreads: [MessageThread] 7 | public let urls: UrlsEnvelope 8 | 9 | public struct UrlsEnvelope { 10 | public let api: ApiEnvelope 11 | 12 | public struct ApiEnvelope { 13 | public let moreMessageThreads: String 14 | } 15 | } 16 | } 17 | 18 | extension MessageThreadsEnvelope: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | return curry(MessageThreadsEnvelope.init) 21 | <^> json <|| "message_threads" 22 | <*> json <| "urls" 23 | } 24 | } 25 | 26 | extension MessageThreadsEnvelope.UrlsEnvelope: Decodable { 27 | public static func decode(_ json: JSON) -> Decoded { 28 | return curry(MessageThreadsEnvelope.UrlsEnvelope.init) 29 | <^> json <| "api" 30 | } 31 | } 32 | 33 | extension MessageThreadsEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 34 | public static func decode(_ json: JSON) -> Decoded { 35 | return curry(MessageThreadsEnvelope.UrlsEnvelope.ApiEnvelope.init) 36 | <^> json <| "more_message_threads" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/Param.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | 3 | /// Represents a way to paramterize a model by either an `id` integer or `slug` string. 4 | public enum Param { 5 | case id(Int) 6 | case slug(String) 7 | 8 | /// Returns the `id` of the param if it is of type `.id`. 9 | public var id: Int? { 10 | if case let .id(id) = self { 11 | return id 12 | } 13 | return nil 14 | } 15 | 16 | /// Returns the `slug` of the param if it is of type `.slug`. 17 | public var slug: String? { 18 | if case let .slug(slug) = self { 19 | return slug 20 | } 21 | return nil 22 | } 23 | 24 | /// Returns a value suitable for interpolating into a URL. 25 | public var urlComponent: String { 26 | switch self { 27 | case let .id(id): 28 | return String(id) 29 | case let .slug(slug): 30 | return slug 31 | } 32 | } 33 | 34 | public var escapedUrlComponent: String { 35 | switch self { 36 | case let .id(id): 37 | return String(id) 38 | case let .slug(slug): 39 | return encodeForRFC3986(slug) ?? "" 40 | } 41 | } 42 | } 43 | 44 | extension Param: Equatable {} 45 | public func == (lhs: Param, rhs: Param) -> Bool { 46 | 47 | switch (lhs, rhs) { 48 | case let (.id(lhs), .id(rhs)): 49 | return lhs == rhs 50 | case let (.slug(lhs), .slug(rhs)): 51 | return lhs == rhs 52 | case let (.id(lhs), .slug(rhs)): 53 | return String(lhs) == rhs 54 | case let (.slug(lhs), .id(rhs)): 55 | return lhs == String(rhs) 56 | } 57 | } 58 | 59 | extension Param: Decodable { 60 | public static func decode(_ json: JSON) -> Decoded { 61 | switch json { 62 | case let .string(slug): 63 | return .success(.slug(slug)) 64 | case let .number(number): 65 | return .success(.id(number.intValue)) 66 | default: 67 | return .failure(.custom("Param must be a number or string.")) 68 | } 69 | } 70 | } 71 | 72 | private let allowableRFC3986: CharacterSet = { 73 | var set = CharacterSet.alphanumerics 74 | set.insert(charactersIn: "-._~/?") 75 | return set 76 | }() 77 | 78 | private func encodeForRFC3986(_ str: String) -> String? { 79 | return str.addingPercentEncoding(withAllowedCharacters: allowableRFC3986) 80 | } 81 | -------------------------------------------------------------------------------- /KsApi/models/Project.CountryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class ProjectCountryTests: XCTestCase { 5 | 6 | func testEquatable() { 7 | XCTAssertEqual(Project.Country.US, Project.Country.US) 8 | XCTAssertNotEqual(Project.Country.US, Project.Country.CA) 9 | XCTAssertNotEqual(Project.Country.US, Project.Country.AU) 10 | XCTAssertNotEqual(Project.Country.DE, Project.Country.ES) 11 | } 12 | 13 | func testDescription() { 14 | XCTAssertNotEqual(Project.Country.US.description, "") 15 | } 16 | 17 | func testJsonDecoding_StandardJSON() { 18 | let decodedCountry = Project.Country.decodeJSONDictionary([ 19 | "country": "US", 20 | "currency": "USD", 21 | "currency_symbol": "$", 22 | "currency_trailing_code": true 23 | ]) 24 | 25 | XCTAssertEqual(.US, decodedCountry.value) 26 | 27 | let country = decodedCountry.value! 28 | XCTAssertEqual(country, Project.Country.decodeJSONDictionary(country.encode()).value) 29 | } 30 | 31 | func testJsonDecoding_ConfigJSON() { 32 | let decodedCountry = Project.Country.decodeJSONDictionary([ 33 | "name": "US", 34 | "currency_code": "USD", 35 | "currency_symbol": "$", 36 | "trailing_code": true 37 | ]) 38 | 39 | XCTAssertEqual(.US, decodedCountry.value) 40 | 41 | let country = decodedCountry.value! 42 | XCTAssertEqual(country, Project.Country.decodeJSONDictionary(country.encode()).value) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /KsApi/models/Project.PhotoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class ProjectPhotoTests: XCTestCase { 5 | 6 | func testJSONParsing_WithPartialData() { 7 | let photo = Project.Photo.decodeJSONDictionary([ 8 | "full": "http://www.kickstarter.com/full.jpg", 9 | "med": "http://www.kickstarter.com/med.jpg", 10 | ]) 11 | 12 | XCTAssertNotNil(photo.error) 13 | } 14 | 15 | func testJSONParsing_WithMissing1024() { 16 | let photo = Project.Photo.decodeJSONDictionary([ 17 | "full": "http://www.kickstarter.com/full.jpg", 18 | "med": "http://www.kickstarter.com/med.jpg", 19 | "small": "http://www.kickstarter.com/small.jpg", 20 | ]) 21 | 22 | XCTAssertNil(photo.error) 23 | XCTAssertEqual(photo.value?.full, "http://www.kickstarter.com/full.jpg") 24 | XCTAssertEqual(photo.value?.med, "http://www.kickstarter.com/med.jpg") 25 | XCTAssertEqual(photo.value?.small, "http://www.kickstarter.com/small.jpg") 26 | XCTAssertNil(photo.value?.size1024x768) 27 | } 28 | 29 | func testJSONParsing_WithFullData() { 30 | let photo = Project.Photo.decodeJSONDictionary([ 31 | "full": "http://www.kickstarter.com/full.jpg", 32 | "med": "http://www.kickstarter.com/med.jpg", 33 | "small": "http://www.kickstarter.com/small.jpg", 34 | "1024x768": "http://www.kickstarter.com/1024x768.jpg", 35 | ]) 36 | 37 | XCTAssertNil(photo.error) 38 | XCTAssertEqual(photo.value?.full, "http://www.kickstarter.com/full.jpg") 39 | XCTAssertEqual(photo.value?.med, "http://www.kickstarter.com/med.jpg") 40 | XCTAssertEqual(photo.value?.small, "http://www.kickstarter.com/small.jpg") 41 | XCTAssertEqual(photo.value?.size1024x768, "http://www.kickstarter.com/1024x768.jpg") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /KsApi/models/Project.Video.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | extension Project { 6 | public struct Video { 7 | public let id: Int 8 | public let high: String 9 | } 10 | } 11 | 12 | extension Project.Video: Decodable { 13 | public static func decode(_ json: JSON) -> Decoded { 14 | return curry(Project.Video.init) 15 | <^> json <| "id" 16 | <*> json <| "high" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /KsApi/models/Project.VideoTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class ProjectVideoTests: XCTestCase { 5 | 6 | func testJsonParsing_WithFullData() { 7 | let video = Project.Video.decodeJSONDictionary([ 8 | "id": 1, 9 | "high": "kickstarter.com/video.mp4" 10 | ]) 11 | 12 | XCTAssertNil(video.error) 13 | XCTAssertEqual(video.value?.id, 1) 14 | XCTAssertEqual(video.value?.high, "kickstarter.com/video.mp4") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /KsApi/models/ProjectActivityEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ProjectActivityEnvelope { 6 | public let activities: [Activity] 7 | public let urls: UrlsEnvelope 8 | 9 | public struct UrlsEnvelope { 10 | public let api: ApiEnvelope 11 | 12 | public struct ApiEnvelope { 13 | public let moreActivities: String 14 | } 15 | } 16 | } 17 | 18 | extension ProjectActivityEnvelope: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | return curry(ProjectActivityEnvelope.init) 21 | <^> json <|| "activities" 22 | <*> json <| "urls" 23 | } 24 | } 25 | 26 | extension ProjectActivityEnvelope.UrlsEnvelope: Decodable { 27 | public static func decode(_ json: JSON) -> Decoded { 28 | return curry(ProjectActivityEnvelope.UrlsEnvelope.init) 29 | <^> json <| "api" 30 | } 31 | } 32 | 33 | extension ProjectActivityEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 34 | public static func decode(_ json: JSON) -> Decoded { 35 | return curry(ProjectActivityEnvelope.UrlsEnvelope.ApiEnvelope.init) 36 | <^> (json <| "more_activities" <|> .success("")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/ProjectNotification.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | import Foundation 5 | 6 | public struct ProjectNotification { 7 | public let email: Bool 8 | public let id: Int 9 | public let mobile: Bool 10 | public let project: Project 11 | 12 | public struct Project { 13 | public let id: Int 14 | public let name: String 15 | } 16 | } 17 | 18 | extension ProjectNotification: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | let create = curry(ProjectNotification.init) 21 | return create 22 | <^> json <| "email" 23 | <*> json <| "id" 24 | <*> json <| "mobile" 25 | <*> json <| "project" 26 | } 27 | } 28 | 29 | extension ProjectNotification.Project: Decodable { 30 | public static func decode(_ json: JSON) -> Decoded { 31 | return curry(ProjectNotification.Project.init) 32 | <^> json <| "id" 33 | <*> json <| "name" 34 | } 35 | } 36 | 37 | extension ProjectNotification: Equatable {} 38 | public func == (lhs: ProjectNotification, rhs: ProjectNotification) -> Bool { 39 | return lhs.id == rhs.id 40 | } 41 | 42 | extension ProjectNotification.Project: Equatable {} 43 | public func == (lhs: ProjectNotification.Project, rhs: ProjectNotification.Project) -> Bool { 44 | return lhs.id == rhs.id 45 | } 46 | -------------------------------------------------------------------------------- /KsApi/models/ProjectsEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ProjectsEnvelope { 6 | public let projects: [Project] 7 | public let urls: UrlsEnvelope 8 | 9 | public struct UrlsEnvelope { 10 | public let api: ApiEnvelope 11 | 12 | public struct ApiEnvelope { 13 | public let moreProjects: String 14 | } 15 | } 16 | } 17 | 18 | extension ProjectsEnvelope: Decodable { 19 | public static func decode(_ json: JSON) -> Decoded { 20 | return curry(ProjectsEnvelope.init) 21 | <^> json <|| "projects" 22 | <*> json <| "urls" 23 | } 24 | } 25 | 26 | extension ProjectsEnvelope.UrlsEnvelope: Decodable { 27 | public static func decode(_ json: JSON) -> Decoded { 28 | return curry(ProjectsEnvelope.UrlsEnvelope.init) 29 | <^> json <| "api" 30 | } 31 | } 32 | 33 | extension ProjectsEnvelope.UrlsEnvelope.ApiEnvelope: Decodable { 34 | public static func decode(_ json: JSON) -> Decoded { 35 | return curry(ProjectsEnvelope.UrlsEnvelope.ApiEnvelope.init) 36 | <^> (json <| "more_projects" <|> .success("")) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /KsApi/models/PushEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct PushEnvelope { 6 | public let activity: Activity? 7 | public let aps: ApsEnvelope 8 | public let forCreator: Bool? 9 | public let liveStream: LiveStream? 10 | public let message: Message? 11 | public let project: Project? 12 | public let survey: Survey? 13 | public let update: Update? 14 | 15 | public struct Activity { 16 | public let category: KsApi.Activity.Category 17 | public let commentId: Int? 18 | public let id: Int 19 | public let projectId: Int? 20 | public let projectPhoto: String? 21 | public let updateId: Int? 22 | public let userPhoto: String? 23 | } 24 | 25 | public struct ApsEnvelope { 26 | public let alert: String 27 | } 28 | 29 | public struct LiveStream { 30 | public let id: Int 31 | } 32 | 33 | public struct Message { 34 | public let messageThreadId: Int 35 | public let projectId: Int 36 | } 37 | 38 | public struct Project { 39 | public let id: Int 40 | public let photo: String? 41 | } 42 | 43 | public struct Survey { 44 | public let id: Int 45 | public let projectId: Int 46 | } 47 | 48 | public struct Update { 49 | public let id: Int 50 | public let projectId: Int 51 | } 52 | } 53 | 54 | extension PushEnvelope: Decodable { 55 | public static func decode(_ json: JSON) -> Decoded { 56 | let create = curry(PushEnvelope.init) 57 | 58 | let update: Decoded = json <| "update" <|> json <| "post" 59 | let optionalUpdate: Decoded = update.map(Optional.some) <|> .success(nil) 60 | 61 | let tmp = create 62 | <^> json <|? "activity" 63 | <*> json <| "aps" 64 | <*> json <|? "for_creator" 65 | <*> json <|? "live_stream" 66 | return tmp 67 | <*> json <|? "message" 68 | <*> json <|? "project" 69 | <*> json <|? "survey" 70 | <*> optionalUpdate 71 | } 72 | } 73 | 74 | extension PushEnvelope.Activity: Decodable { 75 | public static func decode(_ json: JSON) -> Decoded { 76 | let create = curry(PushEnvelope.Activity.init) 77 | let tmp = create 78 | <^> json <| "category" 79 | <*> json <|? "comment_id" 80 | <*> json <| "id" 81 | <*> json <|? "project_id" 82 | return tmp 83 | <*> json <|? "project_photo" 84 | <*> json <|? "update_id" 85 | <*> json <|? "user_photo" 86 | } 87 | } 88 | 89 | extension PushEnvelope.ApsEnvelope: Decodable { 90 | public static func decode(_ json: JSON) -> Decoded { 91 | return curry(PushEnvelope.ApsEnvelope.init) 92 | <^> json <| "alert" 93 | } 94 | } 95 | 96 | extension PushEnvelope.LiveStream: Decodable { 97 | public static func decode(_ json: JSON) -> Decoded { 98 | return curry(PushEnvelope.LiveStream.init) 99 | <^> json <| "id" 100 | } 101 | } 102 | 103 | extension PushEnvelope.Message: Decodable { 104 | public static func decode(_ json: JSON) -> Decoded { 105 | return curry(PushEnvelope.Message.init) 106 | <^> json <| "message_thread_id" 107 | <*> json <| "project_id" 108 | } 109 | } 110 | 111 | extension PushEnvelope.Project: Decodable { 112 | public static func decode(_ json: JSON) -> Decoded { 113 | return curry(PushEnvelope.Project.init) 114 | <^> json <| "id" 115 | <*> json <|? "photo" 116 | } 117 | } 118 | 119 | extension PushEnvelope.Survey: Decodable { 120 | public static func decode(_ json: JSON) -> Decoded { 121 | return curry(PushEnvelope.Survey.init) 122 | <^> json <| "id" 123 | <*> json <| "project_id" 124 | } 125 | } 126 | 127 | extension PushEnvelope.Update: Decodable { 128 | public static func decode(_ json: JSON) -> Decoded { 129 | return curry(PushEnvelope.Update.init) 130 | <^> json <| "id" 131 | <*> json <| "project_id" 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /KsApi/models/PushEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Prelude 4 | 5 | final class PushEnvelopeTests: XCTestCase { 6 | func testDecode_Update_WithUpdateKey() { 7 | let decodedEnvelope = PushEnvelope.decodeJSONDictionary([ 8 | "aps": [ 9 | "alert": "Hi" 10 | ], 11 | "update": [ 12 | "id": 1, 13 | "project_id": 2 14 | ] 15 | ]) 16 | let envelope = decodedEnvelope.value 17 | 18 | XCTAssertNil(decodedEnvelope.error) 19 | XCTAssertNotNil(envelope?.update) 20 | XCTAssertEqual(1, envelope?.update?.id) 21 | XCTAssertEqual(2, envelope?.update?.projectId) 22 | } 23 | 24 | func testDecode_Update_WithPostKey() { 25 | let decodedEnvelope = PushEnvelope.decodeJSONDictionary([ 26 | "aps": [ 27 | "alert": "Hi" 28 | ], 29 | "post": [ 30 | "id": 1, 31 | "project_id": 2 32 | ] 33 | ]) 34 | let envelope = decodedEnvelope.value 35 | 36 | XCTAssertNil(decodedEnvelope.error) 37 | XCTAssertNotNil(envelope?.update) 38 | XCTAssertEqual(1, envelope?.update?.id) 39 | XCTAssertEqual(2, envelope?.update?.projectId) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /KsApi/models/Reward.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | import Prelude 5 | 6 | public struct Reward { 7 | public let backersCount: Int? 8 | public let description: String 9 | public let endsAt: TimeInterval? 10 | public let estimatedDeliveryOn: TimeInterval? 11 | public let id: Int 12 | public let limit: Int? 13 | public let minimum: Int 14 | public let remaining: Int? 15 | public let rewardsItems: [RewardsItem] 16 | public let shipping: Shipping 17 | public let startsAt: TimeInterval? 18 | public let title: String? 19 | 20 | /// Returns `true` is this is the "fake" "No reward" reward. 21 | public var isNoReward: Bool { 22 | return self.id == Reward.noReward.id 23 | } 24 | 25 | public struct Shipping { 26 | public let enabled: Bool 27 | public let preference: Preference? 28 | public let summary: String? 29 | 30 | public enum Preference: String { 31 | case none 32 | case restricted 33 | case unrestricted 34 | } 35 | } 36 | } 37 | 38 | extension Reward: Equatable {} 39 | public func == (lhs: Reward, rhs: Reward) -> Bool { 40 | return lhs.id == rhs.id 41 | } 42 | 43 | private let minimumAndIdComparator = Reward.lens.minimum.comparator <> Reward.lens.id.comparator 44 | 45 | extension Reward: Comparable {} 46 | public func < (lhs: Reward, rhs: Reward) -> Bool { 47 | return minimumAndIdComparator.isOrdered(lhs, rhs) 48 | } 49 | 50 | extension Reward: Decodable { 51 | public static func decode(_ json: JSON) -> Decoded { 52 | let create = curry(Reward.init) 53 | let tmp1 = create 54 | <^> json <|? "backers_count" 55 | <*> (json <| "description" <|> json <| "reward") 56 | <*> json <|? "ends_at" 57 | <*> json <|? "estimated_delivery_on" 58 | let tmp2 = tmp1 59 | <*> json <| "id" 60 | <*> json <|? "limit" 61 | <*> json <| "minimum" 62 | <*> json <|? "remaining" 63 | return tmp2 64 | <*> ((json <|| "rewards_items") <|> .success([])) 65 | <*> Reward.Shipping.decode(json) 66 | <*> json <|? "starts_at" 67 | <*> json <|? "title" 68 | } 69 | } 70 | 71 | extension Reward.Shipping: Decodable { 72 | public static func decode(_ json: JSON) -> Decoded { 73 | return curry(Reward.Shipping.init) 74 | <^> (json <| "shipping_enabled" <|> .success(false)) 75 | <*> json <|? "shipping_preference" 76 | <*> json <|? "shipping_summary" 77 | } 78 | } 79 | 80 | extension Reward.Shipping.Preference: Decodable {} 81 | -------------------------------------------------------------------------------- /KsApi/models/RewardTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Prelude 4 | 5 | final class RewardTests: XCTestCase { 6 | 7 | func testIsNoReward() { 8 | XCTAssertEqual(Reward.noReward.isNoReward, true) 9 | XCTAssertEqual(Reward.template.isNoReward, false) 10 | } 11 | 12 | func testEquatable() { 13 | XCTAssertEqual(Reward.template, Reward.template) 14 | XCTAssertNotEqual(Reward.template, Reward.template |> Reward.lens.id %~ { $0 + 1}) 15 | XCTAssertNotEqual(Reward.template, Reward.noReward) 16 | } 17 | 18 | func testComparable() { 19 | let reward1 = Reward.template |> Reward.lens.id .~ 1 <> Reward.lens.minimum .~ 10 20 | let reward2 = Reward.template |> Reward.lens.id .~ 4 <> Reward.lens.minimum .~ 30 21 | let reward3 = Reward.template |> Reward.lens.id .~ 3 <> Reward.lens.minimum .~ 20 22 | let reward4 = Reward.template |> Reward.lens.id .~ 2 <> Reward.lens.minimum .~ 30 23 | 24 | let rewards = [reward1, reward2, reward3, reward4] 25 | let sorted = rewards.sorted() 26 | 27 | XCTAssertEqual(sorted, [reward1, reward3, reward4, reward2]) 28 | } 29 | 30 | func testJsonParsing_WithMinimalData_AndDescription() { 31 | let reward = Reward.decodeJSONDictionary([ 32 | "id": 1, 33 | "minimum": 10, 34 | "description": "cool stuff" 35 | ]) 36 | 37 | XCTAssertNil(reward.error) 38 | XCTAssertEqual(reward.value?.id, 1) 39 | XCTAssertEqual(reward.value?.description, "cool stuff") 40 | XCTAssertNotNil(reward.value?.shipping) 41 | XCTAssertEqual(false, reward.value?.shipping.enabled) 42 | 43 | } 44 | 45 | func testJsonParsing_WithMinimalData_AndReward() { 46 | let reward = Reward.decodeJSONDictionary([ 47 | "id": 1, 48 | "minimum": 10, 49 | "reward": "cool stuff" 50 | ]) 51 | 52 | XCTAssertNil(reward.error) 53 | XCTAssertEqual(reward.value?.id, 1) 54 | XCTAssertEqual(reward.value?.minimum, 10) 55 | XCTAssertEqual(reward.value?.description, "cool stuff") 56 | } 57 | 58 | func testJsonParsing_WithFullData() { 59 | let reward = Reward.decodeJSONDictionary([ 60 | "id": 1, 61 | "description": "Some reward", 62 | "minimum": 10, 63 | "backers_count": 10 64 | ]) 65 | 66 | XCTAssertNotNil(reward) 67 | XCTAssertEqual(reward.value?.id, 1) 68 | XCTAssertEqual(reward.value?.description, "Some reward") 69 | XCTAssertEqual(reward.value?.minimum, 10) 70 | XCTAssertEqual(reward.value?.backersCount, 10) 71 | } 72 | 73 | func testJsonDecoding_WithShipping() { 74 | 75 | let reward = Reward.decodeJSONDictionary([ 76 | "id": 1, 77 | "description": "Some reward", 78 | "minimum": 10, 79 | "backers_count": 10, 80 | "shipping_enabled": true, 81 | "shipping_preference": "unrestricted", 82 | "shipping_summary": "Ships anywhere in the world." 83 | ]) 84 | 85 | XCTAssertNotNil(reward) 86 | XCTAssertEqual(reward.value?.id, 1) 87 | XCTAssertEqual(reward.value?.description, "Some reward") 88 | XCTAssertEqual(reward.value?.minimum, 10) 89 | XCTAssertEqual(reward.value?.backersCount, 10) 90 | XCTAssertEqual(true, reward.value?.shipping.enabled) 91 | XCTAssertEqual(.unrestricted, reward.value?.shipping.preference) 92 | XCTAssertEqual("Ships anywhere in the world.", reward.value?.shipping.summary) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /KsApi/models/RewardsItem.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct RewardsItem { 6 | public let id: Int 7 | public let item: Item 8 | public let quantity: Int 9 | public let rewardId: Int 10 | } 11 | 12 | extension RewardsItem: Decodable { 13 | public static func decode(_ json: JSON) -> Decoded { 14 | return curry(RewardsItem.init) 15 | <^> json <| "id" 16 | <*> json <| "item" 17 | <*> json <| "quantity" 18 | <*> json <| "reward_id" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/RewardsItemTests.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | import XCTest 3 | @testable import KsApi 4 | 5 | final class RewardsItemTests: XCTestCase { 6 | 7 | func testDecoding() { 8 | let decoded = RewardsItem.decodeJSONDictionary([ 9 | "id": 1, 10 | "item": [ 11 | "description": "Hello", 12 | "id": 1, 13 | "name": "The thing", 14 | "project_id": 1 15 | ], 16 | "quantity": 2, 17 | "reward_id": 3 18 | ]) 19 | 20 | XCTAssertNil(decoded.error) 21 | 22 | XCTAssertEqual(1, decoded.value?.id) 23 | XCTAssertEqual(2, decoded.value?.quantity) 24 | XCTAssertEqual(3, decoded.value?.rewardId) 25 | 26 | XCTAssertEqual("Hello", decoded.value?.item.description) 27 | XCTAssertEqual(1, decoded.value?.item.id) 28 | XCTAssertEqual("The thing", decoded.value?.item.name) 29 | XCTAssertEqual(1, decoded.value?.item.projectId) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /KsApi/models/ShippingRule.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ShippingRule { 6 | public let cost: Double 7 | public let id: Int? 8 | public let location: Location 9 | } 10 | 11 | extension ShippingRule: Decodable { 12 | public static func decode(_ json: JSON) -> Decoded { 13 | return curry(ShippingRule.init) 14 | <^> (json <| "cost" >>- stringToDouble) 15 | <*> json <|? "id" 16 | <*> json <| "location" 17 | } 18 | } 19 | 20 | extension ShippingRule: Equatable {} 21 | public func == (lhs: ShippingRule, rhs: ShippingRule) -> Bool { 22 | // todo: change to compare id once that api is deployed 23 | return lhs.location == rhs.location 24 | } 25 | 26 | private func stringToDouble(_ string: String) -> Decoded { 27 | return Double(string).map(Decoded.success) ?? .success(0) 28 | } 29 | -------------------------------------------------------------------------------- /KsApi/models/ShippingRulesEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct ShippingRulesEnvelope { 6 | public let shippingRules: [ShippingRule] 7 | } 8 | 9 | extension ShippingRulesEnvelope: Decodable { 10 | public static func decode(_ json: JSON) -> Decoded { 11 | return curry(ShippingRulesEnvelope.init) 12 | <^> json <|| "shipping_rules" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /KsApi/models/StarEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct StarEnvelope { 6 | public let user: User 7 | public let project: Project 8 | } 9 | 10 | extension StarEnvelope: Decodable { 11 | public static func decode(_ json: JSON) -> Decoded { 12 | return curry(StarEnvelope.init) 13 | <^> json <| "user" 14 | <*> json <| "project" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /KsApi/models/SubmitApplePayEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct SubmitApplePayEnvelope { 6 | public let thankYouUrl: String 7 | public let status: Int 8 | } 9 | 10 | extension SubmitApplePayEnvelope: Decodable { 11 | public static func decode(_ json: JSON) -> Decoded { 12 | return curry(SubmitApplePayEnvelope.init) 13 | <^> json <| ["data", "thankyou_url"] 14 | <*> ((json <| "status" >>- stringToIntOrZero) <|> (json <| "status")) 15 | } 16 | } 17 | 18 | private func stringToIntOrZero(_ string: String) -> Decoded { 19 | return 20 | Double(string).flatMap(Int.init).map(Decoded.success) 21 | ?? Int(string).map(Decoded.success) 22 | ?? .success(0) 23 | } 24 | -------------------------------------------------------------------------------- /KsApi/models/SubmitApplePayEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class SubmitApplePayEnvelopeTests: XCTestCase { 5 | 6 | func testDecodingWithStringStatus() { 7 | let decoded = SubmitApplePayEnvelope.decodeJSONDictionary( 8 | [ 9 | "data": [ 10 | "thankyou_url": "https://www.kickstarter.com/thanks" 11 | ], 12 | "status": "200" 13 | ] 14 | ) 15 | 16 | XCTAssertNil(decoded.error) 17 | XCTAssertEqual(200, decoded.value?.status) 18 | } 19 | 20 | func testDecodingWithStatus() { 21 | let decoded = SubmitApplePayEnvelope.decodeJSONDictionary( 22 | [ 23 | "data": [ 24 | "thankyou_url": "https://www.kickstarter.com/thanks" 25 | ], 26 | "status": 200 27 | ] 28 | ) 29 | 30 | XCTAssertNil(decoded.error) 31 | XCTAssertEqual(200, decoded.value?.status) 32 | } 33 | 34 | func testDecodingWithMissingStatus() { 35 | 36 | let decoded = SubmitApplePayEnvelope.decodeJSONDictionary( 37 | [ 38 | "data": [ 39 | "thankyou_url": "https://www.kickstarter.com/thanks" 40 | ] 41 | ] 42 | ) 43 | 44 | XCTAssertNotNil(decoded.error) 45 | } 46 | 47 | func testDecodingWithBadStatusData() { 48 | let decoded = SubmitApplePayEnvelope.decodeJSONDictionary( 49 | [ 50 | "data": [ 51 | "thankyou_url": "bad data" 52 | ], 53 | "status": "bad data" 54 | ] 55 | ) 56 | 57 | XCTAssertNil(decoded.error) 58 | XCTAssertEqual(0, decoded.value?.status) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /KsApi/models/SurveyResponse.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct SurveyResponse { 6 | public let answeredAt: TimeInterval? 7 | public let id: Int 8 | public let project: Project? 9 | public let urls: UrlsEnvelope 10 | 11 | public struct UrlsEnvelope { 12 | public let web: WebEnvelope 13 | 14 | public struct WebEnvelope { 15 | public let survey: String 16 | } 17 | } 18 | } 19 | 20 | extension SurveyResponse: Equatable {} 21 | public func == (lhs: SurveyResponse, rhs: SurveyResponse) -> Bool { 22 | return lhs.id == rhs.id 23 | } 24 | 25 | extension SurveyResponse: Decodable { 26 | public static func decode(_ json: JSON) -> Decoded { 27 | return curry(SurveyResponse.init) 28 | <^> json <|? "answered_at" 29 | <*> json <| "id" 30 | <*> json <|? "project" 31 | <*> json <| "urls" 32 | } 33 | } 34 | 35 | extension SurveyResponse.UrlsEnvelope: Decodable { 36 | public static func decode(_ json: JSON) -> Decoded { 37 | return curry(SurveyResponse.UrlsEnvelope.init) 38 | <^> json <| "web" 39 | } 40 | } 41 | 42 | extension SurveyResponse.UrlsEnvelope.WebEnvelope: Decodable { 43 | public static func decode(_ json: JSON) -> Decoded { 44 | return curry(SurveyResponse.UrlsEnvelope.WebEnvelope.init) 45 | <^> json <| "survey" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /KsApi/models/SurveyResponseTests.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | @testable import KsApi 3 | import XCTest 4 | 5 | final internal class SurveyResponseTests: XCTestCase { 6 | func testJSONDecoding() { 7 | let decoded = SurveyResponse.decodeJSONDictionary([ 8 | "id": 1, 9 | "urls": [ 10 | "web": [ 11 | "survey": "http://" 12 | ] 13 | ] 14 | ]) 15 | 16 | XCTAssertNil(decoded.error) 17 | XCTAssertNotNil(decoded.value) 18 | XCTAssertEqual(1, decoded.value?.id) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/Update.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Argo 3 | import Curry 4 | import Runes 5 | 6 | public struct Update { 7 | public let body: String? 8 | public let commentsCount: Int? 9 | public let hasLiked: Bool? 10 | public let id: Int 11 | public let isPublic: Bool 12 | public let likesCount: Int? 13 | public let projectId: Int 14 | public let publishedAt: TimeInterval? 15 | public let sequence: Int 16 | public let title: String 17 | public let urls: UrlsEnvelope 18 | public let user: User? 19 | public let visible: Bool? 20 | 21 | public struct UrlsEnvelope { 22 | public let web: WebEnvelope 23 | 24 | public struct WebEnvelope { 25 | public let update: String 26 | } 27 | } 28 | } 29 | 30 | extension Update: Equatable { 31 | } 32 | public func == (lhs: Update, rhs: Update) -> Bool { 33 | return lhs.id == rhs.id 34 | } 35 | 36 | extension Update: Decodable { 37 | 38 | public static func decode(_ json: JSON) -> Decoded { 39 | let create = curry(Update.init) 40 | let tmp1 = create 41 | <^> json <|? "body" 42 | <*> json <|? "comments_count" 43 | <*> json <|? "has_liked" 44 | let tmp2 = tmp1 45 | <*> json <| "id" 46 | <*> json <| "public" 47 | <*> json <|? "likes_count" 48 | let tmp3 = tmp2 49 | <*> json <| "project_id" 50 | <*> json <|? "published_at" 51 | <*> json <| "sequence" 52 | <*> (json <| "title" <|> .success("")) 53 | return tmp3 54 | <*> json <| "urls" 55 | <*> json <|? "user" 56 | <*> json <|? "visible" 57 | } 58 | } 59 | 60 | extension Update.UrlsEnvelope: Decodable { 61 | static public func decode(_ json: JSON) -> Decoded { 62 | return curry(Update.UrlsEnvelope.init) 63 | <^> json <| "web" 64 | } 65 | } 66 | 67 | extension Update.UrlsEnvelope.WebEnvelope: Decodable { 68 | static public func decode(_ json: JSON) -> Decoded { 69 | return curry(Update.UrlsEnvelope.WebEnvelope.init) 70 | <^> json <| "update" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /KsApi/models/UpdateDraft.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct UpdateDraft { 6 | public let update: Update 7 | public let images: [Image] 8 | public let video: Video? 9 | 10 | public enum Attachment { 11 | case image(Image) 12 | case video(Video) 13 | } 14 | 15 | public struct Image { 16 | public let id: Int 17 | public let thumb: String 18 | public let full: String 19 | } 20 | 21 | public struct Video { 22 | public let id: Int 23 | public let status: Status 24 | public let frame: String 25 | 26 | public enum Status: String { 27 | case processing 28 | case failed 29 | case successful 30 | } 31 | } 32 | } 33 | 34 | extension UpdateDraft: Equatable {} 35 | public func == (lhs: UpdateDraft, rhs: UpdateDraft) -> Bool { 36 | return lhs.update.id == rhs.update.id 37 | } 38 | 39 | extension UpdateDraft.Attachment { 40 | public var id: Int { 41 | switch self { 42 | case let .image(image): 43 | return image.id 44 | case let .video(video): 45 | return video.id 46 | } 47 | } 48 | 49 | public var thumbUrl: String { 50 | switch self { 51 | case let .image(image): 52 | return image.full 53 | case let .video(video): 54 | return video.frame 55 | } 56 | } 57 | } 58 | 59 | extension UpdateDraft.Attachment: Equatable {} 60 | public func == (lhs: UpdateDraft.Attachment, rhs: UpdateDraft.Attachment) -> Bool { 61 | return lhs.id == rhs.id 62 | } 63 | 64 | extension UpdateDraft: Decodable { 65 | public static func decode(_ json: JSON) -> Decoded { 66 | return curry(UpdateDraft.init) 67 | <^> Update.decode(json) 68 | <*> json <|| "images" 69 | <*> json <|? "video" 70 | } 71 | } 72 | 73 | extension UpdateDraft.Image: Decodable { 74 | public static func decode(_ json: JSON) -> Decoded { 75 | return curry(UpdateDraft.Image.init) 76 | <^> json <| "id" 77 | <*> json <| "thumb" 78 | <*> json <| "full" 79 | } 80 | } 81 | 82 | extension UpdateDraft.Video: Decodable { 83 | public static func decode(_ json: JSON) -> Decoded { 84 | return curry(UpdateDraft.Video.init) 85 | <^> json <| "id" 86 | <*> json <| "status" 87 | <*> json <| "frame" 88 | } 89 | } 90 | 91 | extension UpdateDraft.Video.Status: Decodable { 92 | } 93 | -------------------------------------------------------------------------------- /KsApi/models/UpdateDraftTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Prelude 4 | 5 | final class UpdateDraftTests: XCTestCase { 6 | 7 | func testJSONParsing_WithCompleteData() { 8 | 9 | let decoded = UpdateDraft.decodeJSONDictionary([ 10 | "body": "world", 11 | "id": 1, 12 | "public": true, 13 | "project_id": 2, 14 | "sequence": 3, 15 | "title": "hello", 16 | "visible": true, 17 | "urls": [ 18 | "web": [ 19 | "update": "https://www.kickstarter.com/projects/udoo/udoo-x86/posts/1571540" 20 | ] 21 | ], 22 | "images": [["id": 3, "thumb": "thumb.jpg", "full": "full.jpg"]], 23 | "video": ["id": 4, "frame": "frame.jpg", "status": "successful"] 24 | ]) 25 | 26 | XCTAssertNil(decoded.error) 27 | let draft = decoded.value 28 | XCTAssertEqual(1, draft?.update.id) 29 | XCTAssertEqual(3, draft?.images.first?.id) 30 | XCTAssertEqual(4, draft?.video?.id) 31 | } 32 | 33 | func testAttachmentThumbUrl() { 34 | 35 | let image = UpdateDraft.Attachment.image(.template |> UpdateDraft.Image.lens.full .~ "full.jpg") 36 | XCTAssertEqual("full.jpg", image.thumbUrl) 37 | 38 | let video = UpdateDraft.Attachment.video(.template |> UpdateDraft.Video.lens.frame .~ "frame.jpg") 39 | XCTAssertEqual("frame.jpg", video.thumbUrl) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /KsApi/models/UpdatePledgeEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | import Curry 3 | import Runes 4 | 5 | public struct UpdatePledgeEnvelope { 6 | public let newCheckoutUrl: String? 7 | public let status: Int 8 | } 9 | 10 | extension UpdatePledgeEnvelope: Decodable { 11 | public static func decode(_ json: JSON) -> Decoded { 12 | return curry(UpdatePledgeEnvelope.init) 13 | <^> json <|? ["data", "new_checkout_url"] 14 | <*> ((json <| "status" >>- stringToIntOrZero) <|> (json <| "status")) 15 | } 16 | } 17 | 18 | private func stringToIntOrZero(_ string: String) -> Decoded { 19 | return 20 | Double(string).flatMap(Int.init).map(Decoded.success) 21 | ?? Int(string).map(Decoded.success) 22 | ?? .success(0) 23 | } 24 | -------------------------------------------------------------------------------- /KsApi/models/UpdatePledgeEnvelopeTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class UpdatePledgeEnvelopeTests: XCTestCase { 5 | 6 | func testDecodingWithStringStatus() { 7 | let decoded = UpdatePledgeEnvelope.decodeJSONDictionary(["status": "200"]) 8 | XCTAssertNil(decoded.error) 9 | XCTAssertEqual(200, decoded.value?.status) 10 | } 11 | 12 | func testDecodingWithIntStatus() { 13 | let decoded = UpdatePledgeEnvelope.decodeJSONDictionary(["status": 200]) 14 | XCTAssertNil(decoded.error) 15 | XCTAssertEqual(200, decoded.value?.status) 16 | } 17 | 18 | func testDecodingWithMissingStatus() { 19 | let decoded = UpdatePledgeEnvelope.decodeJSONDictionary([:]) 20 | XCTAssertNotNil(decoded.error) 21 | } 22 | 23 | func testDecodingWithBadStatusData() { 24 | let decoded = UpdatePledgeEnvelope.decodeJSONDictionary(["status": "bad data"]) 25 | XCTAssertNil(decoded.error) 26 | XCTAssertEqual(0, decoded.value?.status) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /KsApi/models/UpdateTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Argo 4 | import Prelude 5 | 6 | final internal class UpdateTests: XCTestCase { 7 | 8 | func testEquatable() { 9 | XCTAssertEqual(Update.template, Update.template) 10 | XCTAssertNotEqual(Update.template, Update.template |> Update.lens.id %~ { $0 + 1 }) 11 | } 12 | 13 | func testJSONDecoding_WithBadData() { 14 | let update = Update.decodeJSONDictionary([ 15 | "body": "world", 16 | ]) 17 | 18 | XCTAssertNotNil(update.error) 19 | } 20 | 21 | func testJSONDecoding_WithGoodData() { 22 | let update = Update.decodeJSONDictionary([ 23 | "body": "world", 24 | "id": 1, 25 | "public": true, 26 | "project_id": 2, 27 | "sequence": 3, 28 | "title": "hello", 29 | "visible": true, 30 | "urls": [ 31 | "web": [ 32 | "update": "https://www.kickstarter.com/projects/udoo/udoo-x86/posts/1571540" 33 | ] 34 | ] 35 | ]) 36 | 37 | XCTAssertNil(update.error) 38 | XCTAssertEqual(1, update.value?.id) 39 | } 40 | 41 | func testJSONDecoding_WithNestedGoodData() { 42 | let update = Update.decodeJSONDictionary([ 43 | "body": "world", 44 | "id": 1, 45 | "public": true, 46 | "project_id": 2, 47 | "sequence": 3, 48 | "title": "hello", 49 | "user": [ 50 | "id": 2, 51 | "name": "User", 52 | "avatar": [ 53 | "medium": "img.jpg", 54 | "small": "img.jpg", 55 | "large": "img.jpg", 56 | ] 57 | ], 58 | "visible": true, 59 | "urls": [ 60 | "web": [ 61 | "update": "https://www.kickstarter.com/projects/udoo/udoo-x86/posts/1571540" 62 | ] 63 | ] 64 | ]) 65 | 66 | XCTAssertNil(update.error) 67 | XCTAssertEqual(1, update.value?.id) 68 | XCTAssertEqual(2, update.value?.user?.id) 69 | XCTAssertEqual("https://www.kickstarter.com/projects/udoo/udoo-x86/posts/1571540", 70 | update.value?.urls.web.update) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /KsApi/models/User.AvatarTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class UserAvatarTests: XCTestCase { 5 | 6 | func testJsonEncoding() { 7 | let json: [String:Any] = [ 8 | "medium": "http://www.kickstarter.com/medium.jpg", 9 | "small": "http://www.kickstarter.com/small.jpg" 10 | ] 11 | let avatar = User.Avatar.decodeJSONDictionary(json) 12 | 13 | XCTAssertEqual(avatar.value?.encode().description, json.description) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/User.NewsletterSubscriptionsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class NewsletterSubscriptionsTests: XCTestCase { 5 | 6 | func testJsonEncoding() { 7 | let json: [String:Any] = [ 8 | "games_newsletter": false, 9 | "promo_newsletter": false, 10 | "happening_newsletter": false, 11 | "weekly_newsletter": false 12 | ] 13 | 14 | let newsletter = User.NewsletterSubscriptions.decodeJSONDictionary(json) 15 | 16 | XCTAssertEqual(newsletter.value?.encode().description, json.description) 17 | 18 | XCTAssertEqual(false, newsletter.value?.weekly) 19 | XCTAssertEqual(false, newsletter.value?.promo) 20 | XCTAssertEqual(false, newsletter.value?.happening) 21 | XCTAssertEqual(false, newsletter.value?.games) 22 | } 23 | 24 | func testJsonEncoding_TrueValues() { 25 | let json: [String:Any] = [ 26 | "games_newsletter": true, 27 | "promo_newsletter": true, 28 | "happening_newsletter": true, 29 | "weekly_newsletter": true 30 | ] 31 | 32 | let newsletter = User.NewsletterSubscriptions.decodeJSONDictionary(json) 33 | 34 | XCTAssertEqual(newsletter.value?.encode().description, json.description) 35 | 36 | XCTAssertEqual(true, newsletter.value?.weekly) 37 | XCTAssertEqual(true, newsletter.value?.promo) 38 | XCTAssertEqual(true, newsletter.value?.happening) 39 | XCTAssertEqual(true, newsletter.value?.games) 40 | } 41 | 42 | func testJsonDecoding() { 43 | let json = User.NewsletterSubscriptions.decodeJSONDictionary([ 44 | "games_newsletter": true, 45 | "happening_newsletter": false, 46 | "promo_newsletter": true, 47 | "weekly_newsletter": false 48 | ]) 49 | 50 | let newsletters = json.value 51 | 52 | XCTAssertEqual(newsletters, 53 | User.NewsletterSubscriptions.decodeJSONDictionary(newsletters?.encode() ?? [:]).value) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /KsApi/models/User.NotificationsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | 4 | final class NotificationsTests: XCTestCase { 5 | func testJsonEncoding() { 6 | let json: [String:Any] = [ 7 | "notify_of_backings": false, 8 | "notify_of_comments": false, 9 | "notify_of_follower": false, 10 | "notify_of_friend_activity": false, 11 | "notify_of_post_likes": false, 12 | "notify_of_updates": false, 13 | "notify_mobile_of_backings": false, 14 | "notify_mobile_of_comments": false, 15 | "notify_mobile_of_follower": false, 16 | "notify_mobile_of_friend_activity": false, 17 | "notify_mobile_of_post_likes": false, 18 | "notify_mobile_of_updates": false 19 | ] 20 | 21 | let notification = User.Notifications.decodeJSONDictionary(json) 22 | 23 | XCTAssertNil(notification.error) 24 | XCTAssertEqual(notification.value?.encode() as NSDictionary?, json as NSDictionary?) 25 | 26 | XCTAssertEqual(false, notification.value?.backings) 27 | XCTAssertEqual(false, notification.value?.comments) 28 | XCTAssertEqual(false, notification.value?.follower) 29 | XCTAssertEqual(false, notification.value?.friendActivity) 30 | XCTAssertEqual(false, notification.value?.postLikes) 31 | XCTAssertEqual(false, notification.value?.updates) 32 | XCTAssertEqual(false, notification.value?.mobileBackings) 33 | XCTAssertEqual(false, notification.value?.mobileComments) 34 | XCTAssertEqual(false, notification.value?.mobileFollower) 35 | XCTAssertEqual(false, notification.value?.mobileFriendActivity) 36 | XCTAssertEqual(false, notification.value?.mobilePostLikes) 37 | XCTAssertEqual(false, notification.value?.mobileUpdates) 38 | } 39 | 40 | func testJsonEncoding_TrueValues() { 41 | let json: [String:Any] = [ 42 | "notify_of_backings": true, 43 | "notify_of_comments": true, 44 | "notify_of_follower": true, 45 | "notify_of_friend_activity": true, 46 | "notify_of_post_likes": true, 47 | "notify_of_updates": true, 48 | "notify_mobile_of_backings": true, 49 | "notify_mobile_of_comments": true, 50 | "notify_mobile_of_follower": true, 51 | "notify_mobile_of_friend_activity": true, 52 | "notify_mobile_of_post_likes": true, 53 | "notify_mobile_of_updates": true 54 | ] 55 | 56 | let notification = User.Notifications.decodeJSONDictionary(json) 57 | 58 | XCTAssertNil(notification.error) 59 | XCTAssertEqual(notification.value?.encode() as NSDictionary?, json as NSDictionary?) 60 | 61 | XCTAssertEqual(true, notification.value?.backings) 62 | XCTAssertEqual(true, notification.value?.comments) 63 | XCTAssertEqual(true, notification.value?.follower) 64 | XCTAssertEqual(true, notification.value?.friendActivity) 65 | XCTAssertEqual(true, notification.value?.postLikes) 66 | XCTAssertEqual(true, notification.value?.updates) 67 | XCTAssertEqual(true, notification.value?.mobileBackings) 68 | XCTAssertEqual(true, notification.value?.mobileComments) 69 | XCTAssertEqual(true, notification.value?.mobileFollower) 70 | XCTAssertEqual(true, notification.value?.mobileFriendActivity) 71 | XCTAssertEqual(true, notification.value?.mobilePostLikes) 72 | XCTAssertEqual(true, notification.value?.mobileUpdates) 73 | } 74 | 75 | func testJsonDecoding() { 76 | let json = User.Notifications.decodeJSONDictionary([ 77 | "notify_of_backings": true, 78 | "notify_of_comments": false, 79 | "notify_of_follower": true, 80 | "notify_of_friend_activity": false, 81 | "notify_of_post_likes": true, 82 | "notify_of_updates": false, 83 | "notify_mobile_of_backings": true, 84 | "notify_mobile_of_comments": false, 85 | "notify_mobile_of_follower": true, 86 | "notify_mobile_of_friend_activity": false, 87 | "notify_mobile_of_post_likes": true, 88 | "notify_mobile_of_updates": false 89 | ]) 90 | 91 | let notifications = json.value 92 | 93 | XCTAssertEqual(notifications, 94 | User.Notifications.decodeJSONDictionary(notifications?.encode() ?? [:]).value) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /KsApi/models/UserTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import KsApi 3 | import Prelude 4 | 5 | // swiftlint:disable force_cast 6 | final class UserTests: XCTestCase { 7 | 8 | func testEquatable() { 9 | XCTAssertEqual(User.template, User.template) 10 | XCTAssertNotEqual(User.template, User.template |> User.lens.id %~ { $0 + 1 }) 11 | } 12 | 13 | func testDescription() { 14 | XCTAssertNotEqual("", User.template.debugDescription) 15 | } 16 | 17 | func testJsonParsing() { 18 | let json: [String:Any] = [ 19 | "id": 1, 20 | "name": "Blob", 21 | "avatar": [ 22 | "medium": "http://www.kickstarter.com/medium.jpg", 23 | "small": "http://www.kickstarter.com/small.jpg" 24 | ], 25 | "backed_projects_count": 2, 26 | "weekly_newsletter": false, 27 | "promo_newsletter": false, 28 | "happening_newsletter": false, 29 | "games_newsletter": false, 30 | "facebook_connected": false, 31 | "ksr_live_token": "token", 32 | "location": [ 33 | "country": "US", 34 | "id": 12, 35 | "displayable_name": "Brooklyn, NY", 36 | "name": "Brooklyn" 37 | ], 38 | "is_friend": false 39 | ] 40 | let decoded = User.decodeJSONDictionary(json) 41 | let user = decoded.value 42 | 43 | XCTAssertNil(decoded.error) 44 | XCTAssertEqual(1, user?.id) 45 | XCTAssertEqual("http://www.kickstarter.com/small.jpg", user?.avatar.small) 46 | XCTAssertEqual(2, user?.stats.backedProjectsCount) 47 | XCTAssertEqual(false, user?.newsletters.weekly) 48 | XCTAssertEqual(false, user?.newsletters.promo) 49 | XCTAssertEqual(false, user?.newsletters.happening) 50 | XCTAssertEqual(false, user?.newsletters.games) 51 | XCTAssertEqual(false, user?.facebookConnected) 52 | XCTAssertEqual(false, user?.isFriend) 53 | XCTAssertEqual("token", user?.liveAuthToken) 54 | XCTAssertNotNil(user?.location) 55 | XCTAssertEqual(json as NSDictionary?, user?.encode() as NSDictionary?) 56 | } 57 | 58 | func testJsonEncoding() { 59 | let json: [String:Any] = [ 60 | "id": 1, 61 | "name": "Blob", 62 | "avatar": [ 63 | "medium": "http://www.kickstarter.com/medium.jpg", 64 | "small": "http://www.kickstarter.com/small.jpg", 65 | "large": "http://www.kickstarter.com/large.jpg" 66 | ], 67 | "backed_projects_count": 2, 68 | "games_newsletter": false, 69 | "happening_newsletter": false, 70 | "promo_newsletter": false, 71 | "weekly_newsletter": false, 72 | "facebook_connected": false, 73 | "ksr_live_token": "token", 74 | "location": [ 75 | "country": "US", 76 | "id": 12, 77 | "displayable_name": "Brooklyn, NY", 78 | "name": "Brooklyn" 79 | ], 80 | "is_friend": false 81 | ] 82 | let user = User.decodeJSONDictionary(json) 83 | 84 | XCTAssertEqual(user.value?.encode() as NSDictionary?, json as NSDictionary?) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /KsApi/models/VoidEnvelope.swift: -------------------------------------------------------------------------------- 1 | import Argo 2 | 3 | public struct VoidEnvelope { 4 | } 5 | 6 | extension VoidEnvelope: Decodable { 7 | public static func decode(_ json: JSON) -> Decoded { 8 | return .success(VoidEnvelope()) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Activity.MemberDataLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Activity.MemberData { 4 | public enum lens { 5 | public static let amount = Lens( 6 | view: { $0.amount }, 7 | set: { Activity.MemberData(amount: $0, backing: $1.backing, oldAmount: $1.oldAmount, 8 | oldRewardId: $1.oldRewardId, newAmount: $1.newAmount, newRewardId: $1.newRewardId, 9 | rewardId: $1.rewardId) } 10 | ) 11 | 12 | public static let backing = Lens( 13 | view: { $0.backing }, 14 | set: { Activity.MemberData(amount: $1.amount, backing: $0, oldAmount: $1.oldAmount, 15 | oldRewardId: $1.oldRewardId, newAmount: $1.newAmount, newRewardId: $1.newRewardId, 16 | rewardId: $1.rewardId) } 17 | ) 18 | 19 | public static let oldAmount = Lens( 20 | view: { $0.oldAmount }, 21 | set: { Activity.MemberData(amount: $1.amount, backing: $1.backing, oldAmount: $0, 22 | oldRewardId: $1.oldRewardId, newAmount: $1.newAmount, newRewardId: $1.newRewardId, 23 | rewardId: $1.rewardId) } 24 | ) 25 | 26 | public static let oldRewardId = Lens( 27 | view: { $0.oldRewardId }, 28 | set: { Activity.MemberData(amount: $1.amount, backing: $1.backing, oldAmount: $1.oldAmount, 29 | oldRewardId: $0, newAmount: $1.newAmount, newRewardId: $1.newRewardId, 30 | rewardId: $1.rewardId) } 31 | ) 32 | 33 | public static let newAmount = Lens( 34 | view: { $0.newAmount }, 35 | set: { Activity.MemberData(amount: $1.amount, backing: $1.backing, oldAmount: $1.oldAmount, 36 | oldRewardId: $1.oldRewardId, newAmount: $0, newRewardId: $1.newRewardId, 37 | rewardId: $1.rewardId) } 38 | ) 39 | 40 | public static let newRewardId = Lens( 41 | view: { $0.newRewardId }, 42 | set: { Activity.MemberData(amount: $1.amount, backing: $1.backing, oldAmount: $1.oldAmount, 43 | oldRewardId: $1.oldRewardId, newAmount: $1.newAmount, newRewardId: $0, 44 | rewardId: $1.rewardId) } 45 | ) 46 | 47 | public static let rewardId = Lens( 48 | view: { $0.rewardId }, 49 | set: { Activity.MemberData(amount: $1.amount, backing: $1.backing, oldAmount: $1.oldAmount, 50 | oldRewardId: $1.oldRewardId, newAmount: $1.newRewardId, newRewardId: $1.newRewardId, 51 | rewardId: $0) } 52 | ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ActivityLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Activity { 4 | public enum lens { 5 | public static let category = Lens( 6 | view: { $0.category }, 7 | set: { Activity(category: $0, comment: $1.comment, createdAt: $1.createdAt, id: $1.id, 8 | memberData: $1.memberData, project: $1.project, update: $1.update, user: $1.user) } 9 | ) 10 | 11 | public static let comment = Lens( 12 | view: { $0.comment }, 13 | set: { Activity(category: $1.category, comment: $0, createdAt: $1.createdAt, id: $1.id, 14 | memberData: $1.memberData, project: $1.project, update: $1.update, user: $1.user) } 15 | ) 16 | 17 | public static let createdAt = Lens( 18 | view: { $0.createdAt }, 19 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $0, id: $1.id, 20 | memberData: $1.memberData, project: $1.project, update: $1.update, user: $1.user) } 21 | ) 22 | 23 | public static let id = Lens( 24 | view: { $0.id }, 25 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $1.createdAt, id: $0, 26 | memberData: $1.memberData, project: $1.project, update: $1.update, user: $1.user) } 27 | ) 28 | 29 | public static let memberData = Lens( 30 | view: { $0.memberData }, 31 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $1.createdAt, id: $1.id, 32 | memberData: $0, project: $1.project, update: $1.update, user: $1.user) } 33 | ) 34 | 35 | public static let project = Lens( 36 | view: { $0.project }, 37 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $1.createdAt, id: $1.id, 38 | memberData: $1.memberData, project: $0, update: $1.update, user: $1.user) } 39 | ) 40 | 41 | public static let update = Lens( 42 | view: { $0.update }, 43 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $1.createdAt, id: $1.id, 44 | memberData: $1.memberData, project: $1.project, update: $0, user: $1.user) } 45 | ) 46 | 47 | public static let user = Lens( 48 | view: { $0.user }, 49 | set: { Activity(category: $1.category, comment: $1.comment, createdAt: $1.createdAt, id: $1.id, 50 | memberData: $1.memberData, project: $1.project, update: $1.update, user: $0) } 51 | ) 52 | } 53 | } 54 | 55 | extension Lens where Whole == Activity, Part == Activity.MemberData { 56 | public var amount: Lens { 57 | return Activity.lens.memberData..Activity.MemberData.lens.amount 58 | } 59 | 60 | public var backing: Lens { 61 | return Activity.lens.memberData..Activity.MemberData.lens.backing 62 | } 63 | 64 | public var oldAmount: Lens { 65 | return Activity.lens.memberData..Activity.MemberData.lens.oldAmount 66 | } 67 | 68 | public var oldRewardId: Lens { 69 | return Activity.lens.memberData..Activity.MemberData.lens.oldRewardId 70 | } 71 | 72 | public var newAmount: Lens { 73 | return Activity.lens.memberData..Activity.MemberData.lens.newAmount 74 | } 75 | 76 | public var newRewardId: Lens { 77 | return Activity.lens.memberData..Activity.MemberData.lens.newRewardId 78 | } 79 | 80 | public var rewardId: Lens { 81 | return Activity.lens.memberData..Activity.MemberData.lens.rewardId 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /KsApi/models/lenses/CategoriesEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension CategoriesEnvelope { 4 | public enum lens { 5 | public static let categories = Lens( 6 | view: { $0.categories }, 7 | set: { part, _ in CategoriesEnvelope(categories: part) } 8 | ) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/lenses/CategoryLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Category { 4 | public enum lens { 5 | public static let id = Lens( 6 | view: { $0.id }, 7 | set: { Category(color: $1.color, id: $0, name: $1.name, parent: $1.parent, parentId: $1.parentId, 8 | position: $1.position, projectsCount: $1.projectsCount, slug: $1.slug) } 9 | ) 10 | 11 | public static let name = Lens( 12 | view: { $0.name }, 13 | set: { Category(color: $1.color, id: $1.id, name: $0, parent: $1.parent, parentId: $1.parentId, 14 | position: $1.position, projectsCount: $1.projectsCount, slug: $1.slug) } 15 | ) 16 | 17 | public static let parent = Lens( 18 | view: { $0.parent }, 19 | set: { Category(color: $1.color, id: $1.id, name: $1.name, parent: $0, parentId: $1.parentId, 20 | position: $1.position, projectsCount: $1.projectsCount, slug: $1.slug) } 21 | ) 22 | 23 | public static let parentId = Lens( 24 | view: { $0.parentId }, 25 | set: { Category(color: $1.color, id: $1.id, name: $1.name, parent: $1.parent, parentId: $0, 26 | position: $1.position, projectsCount: $1.projectsCount, slug: $1.slug) } 27 | ) 28 | 29 | public static let position = Lens( 30 | view: { $0.position }, 31 | set: { Category(color: $1.color, id: $1.id, name: $1.name, parent: $1.parent, 32 | parentId: $1.parentId, position: $0, projectsCount: $1.projectsCount, slug: $1.slug) } 33 | ) 34 | 35 | public static let projectsCount = Lens( 36 | view: { $0.projectsCount }, 37 | set: { Category(color: $1.color, id: $1.id, name: $1.name, parent: $1.parent, 38 | parentId: $1.parentId, position: $1.position, projectsCount: $0, slug: $1.slug) } 39 | ) 40 | 41 | public static let slug = Lens( 42 | view: { $0.slug }, 43 | set: { Category(color: $1.color, id: $1.id, name: $1.name, parent: $1.parent, 44 | parentId: $1.parentId, position: $1.position, projectsCount: $1.projectsCount, slug: $0) } 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /KsApi/models/lenses/CheckoutEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension CheckoutEnvelope { 4 | public enum lens { 5 | public static let state = Lens( 6 | view: { $0.state }, 7 | set: { CheckoutEnvelope(state: $0, stateReason: $1.stateReason) } 8 | ) 9 | public static let stateReason = Lens( 10 | view: { $0.stateReason }, 11 | set: { CheckoutEnvelope(state: $1.state, stateReason: $0) } 12 | ) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /KsApi/models/lenses/CommentLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Comment { 4 | public enum lens { 5 | public static let author = Lens( 6 | view: { $0.author }, 7 | set: { Comment(author: $0, body: $1.body, createdAt: $1.createdAt, deletedAt: $1.deletedAt, id: $1.id) } 8 | ) 9 | 10 | public static let body = Lens( 11 | view: { $0.body }, 12 | set: { Comment(author: $1.author, body: $0, createdAt: $1.createdAt, deletedAt: $1.deletedAt, 13 | id: $1.id) } 14 | ) 15 | 16 | public static let createdAt = Lens( 17 | view: { $0.createdAt }, 18 | set: { Comment(author: $1.author, body: $1.body, createdAt: $0, deletedAt: $1.deletedAt, id: $1.id) } 19 | ) 20 | 21 | public static let deletedAt = Lens( 22 | view: { $0.deletedAt }, 23 | set: { Comment(author: $1.author, body: $1.body, createdAt: $1.createdAt, deletedAt: $0, id: $1.id) } 24 | ) 25 | 26 | public static let id = Lens( 27 | view: { $0.id }, 28 | set: { Comment(author: $1.author, body: $1.body, createdAt: $1.createdAt, deletedAt: $1.deletedAt, 29 | id: $0) } 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ConfigLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Config { 4 | public enum lens { 5 | public static let applePayCountries = Lens( 6 | view: { $0.applePayCountries }, 7 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $0, 8 | countryCode: $1.countryCode, features: $1.features, iTunesLink: $1.iTunesLink, 9 | launchedCountries: $1.launchedCountries, locale: $1.locale, 10 | stripePublishableKey: $1.stripePublishableKey) } 11 | ) 12 | 13 | public static let countryCode = Lens( 14 | view: { $0.countryCode }, 15 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $1.applePayCountries, 16 | countryCode: $0, features: $1.features, iTunesLink: $1.iTunesLink, 17 | launchedCountries: $1.launchedCountries, locale: $1.locale, 18 | stripePublishableKey: $1.stripePublishableKey) } 19 | ) 20 | 21 | public static let features = Lens( 22 | view: { $0.features }, 23 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $1.applePayCountries, 24 | countryCode: $1.countryCode, features: $0, iTunesLink: $1.iTunesLink, 25 | launchedCountries: $1.launchedCountries, locale: $1.locale, 26 | stripePublishableKey: $1.stripePublishableKey) } 27 | ) 28 | 29 | public static let launchedCountries = Lens( 30 | view: { $0.launchedCountries }, 31 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $1.applePayCountries, 32 | countryCode: $1.countryCode, features: $1.features, iTunesLink: $1.iTunesLink, launchedCountries: $0, 33 | locale: $1.locale, stripePublishableKey: $1.stripePublishableKey) } 34 | ) 35 | 36 | public static let locale = Lens( 37 | view: { $0.locale }, 38 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $1.applePayCountries, 39 | countryCode: $1.countryCode, features: $1.features, iTunesLink: $1.iTunesLink, 40 | launchedCountries: $1.launchedCountries, locale: $0, stripePublishableKey: $1.stripePublishableKey) } 41 | ) 42 | 43 | public static let stripePublishableKey = Lens( 44 | view: { $0.stripePublishableKey }, 45 | set: { Config(abExperiments: $1.abExperiments, appId: $1.appId, applePayCountries: $1.applePayCountries, 46 | countryCode: $1.countryCode, features: $1.features, iTunesLink: $1.iTunesLink, 47 | launchedCountries: $1.launchedCountries, locale: $1.locale, stripePublishableKey: $0) } 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /KsApi/models/lenses/CreatePledgeEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension CreatePledgeEnvelope { 4 | public enum lens { 5 | public static let checkoutUrl = Lens( 6 | view: { $0.checkoutUrl }, 7 | set: { .init(checkoutUrl: $0, newCheckoutUrl: $1.newCheckoutUrl, status: $1.status) } 8 | ) 9 | 10 | public static let newCheckoutUrl = Lens( 11 | view: { $0.checkoutUrl }, 12 | set: { .init(checkoutUrl: $1.checkoutUrl, newCheckoutUrl: $0, status: $1.status) } 13 | ) 14 | 15 | public static let status = Lens( 16 | view: { $0.status }, 17 | set: { .init(checkoutUrl: $1.checkoutUrl, newCheckoutUrl: $1.newCheckoutUrl, status: $0) } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/DiscoveryEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension DiscoveryEnvelope { 4 | public enum lens { 5 | public static let projects = Lens( 6 | view: { $0.projects }, 7 | set: { DiscoveryEnvelope(projects: $0, urls: $1.urls, stats: $1.stats) } 8 | ) 9 | public static let urls = Lens( 10 | view: { $0.urls }, 11 | set: { DiscoveryEnvelope(projects: $1.projects, urls: $0, stats: $1.stats) } 12 | ) 13 | public static let stats = Lens( 14 | view: { $0.stats }, 15 | set: { DiscoveryEnvelope(projects: $1.projects, urls: $1.urls, stats: $0) } 16 | ) 17 | } 18 | } 19 | 20 | extension DiscoveryEnvelope.UrlsEnvelope { 21 | public enum lens { 22 | public static let api = Lens( 23 | view: { $0.api }, 24 | set: { part, _ in DiscoveryEnvelope.UrlsEnvelope(api: part) } 25 | ) 26 | } 27 | } 28 | 29 | extension DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope { 30 | public enum lens { 31 | public static let moreProjects = Lens( 32 | view: { $0.moreProjects }, 33 | set: { part, _ in DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope(more_projects: part) } 34 | ) 35 | } 36 | } 37 | 38 | extension DiscoveryEnvelope.StatsEnvelope { 39 | public enum lens { 40 | public static let count = Lens( 41 | view: { $0.count }, 42 | set: { part, _ in DiscoveryEnvelope.StatsEnvelope(count: part) } 43 | ) 44 | } 45 | } 46 | 47 | extension Lens where Whole == DiscoveryEnvelope, Part == DiscoveryEnvelope.UrlsEnvelope { 48 | public var api: Lens { 49 | return DiscoveryEnvelope.lens.urls..DiscoveryEnvelope.UrlsEnvelope.lens.api 50 | } 51 | } 52 | 53 | extension Lens where Whole == DiscoveryEnvelope, Part == DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope { 54 | public var moreProjects: Lens { 55 | return DiscoveryEnvelope.lens.urls.api..DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope.lens.moreProjects 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /KsApi/models/lenses/FindFriendsEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension FindFriendsEnvelope { 4 | public enum lens { 5 | public static let contactsImported = Lens( 6 | view: { $0.contactsImported }, 7 | set: { FindFriendsEnvelope(contactsImported: $0, urls: $1.urls, users: $1.users) } 8 | ) 9 | public static let urls = Lens( 10 | view: { $0.urls }, 11 | set: { FindFriendsEnvelope(contactsImported: $1.contactsImported, urls: $0, users: $1.users) } 12 | ) 13 | public static let users = Lens( 14 | view: { $0.users }, 15 | set: { FindFriendsEnvelope(contactsImported: $1.contactsImported, urls: $1.urls, users: $0) } 16 | ) 17 | } 18 | } 19 | 20 | extension FindFriendsEnvelope.UrlsEnvelope { 21 | public enum lens { 22 | public static let api = Lens( 23 | view: { $0.api }, 24 | set: { part, _ in FindFriendsEnvelope.UrlsEnvelope(api: part) } 25 | ) 26 | } 27 | } 28 | 29 | extension FindFriendsEnvelope.UrlsEnvelope.ApiEnvelope { 30 | public enum lens { 31 | public static let moreProjects = Lens( 32 | view: { $0.moreUsers }, 33 | set: { part, _ in FindFriendsEnvelope.UrlsEnvelope.ApiEnvelope(moreUsers: part) } 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /KsApi/models/lenses/FriendStatsEnvelope.StatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension FriendStatsEnvelope.Stats { 4 | public enum lens { 5 | public static let friendProjectsCount = Lens( 6 | view: { $0.friendProjectsCount }, 7 | set: { FriendStatsEnvelope.Stats(friendProjectsCount: $0, remoteFriendsCount: $1.remoteFriendsCount) } 8 | ) 9 | 10 | public static let remoteFriendsCount = Lens( 11 | view: { $0.remoteFriendsCount }, 12 | set: { FriendStatsEnvelope.Stats(friendProjectsCount: $1.friendProjectsCount, remoteFriendsCount: $0) } 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/lenses/FriendStatsEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension FriendStatsEnvelope { 4 | public enum lens { 5 | public static let stats = Lens( 6 | view: { $0.stats }, 7 | set: { stats, _ in FriendStatsEnvelope(stats: stats) } 8 | ) 9 | } 10 | } 11 | 12 | extension Lens where Whole == FriendStatsEnvelope, Part == FriendStatsEnvelope.Stats { 13 | public var friendProjectsCount: Lens { 14 | return FriendStatsEnvelope.lens.stats..FriendStatsEnvelope.Stats.lens.friendProjectsCount 15 | } 16 | 17 | public var remoteFriendsCount: Lens { 18 | return FriendStatsEnvelope.lens.stats..FriendStatsEnvelope.Stats.lens.remoteFriendsCount 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ItemLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Item { 4 | public enum lens { 5 | public static let description = Lens( 6 | view: { $0.description }, 7 | set: { .init(description: $0, id: $1.id, name: $1.name, 8 | projectId: $1.projectId) } 9 | ) 10 | 11 | public static let id = Lens( 12 | view: { $0.id }, 13 | set: { .init(description: $1.description, id: $0, name: $1.name, 14 | projectId: $1.projectId) } 15 | ) 16 | 17 | public static let name = Lens( 18 | view: { $0.name }, 19 | set: { .init(description: $1.description, id: $1.id, name: $0, 20 | projectId: $1.projectId) } 21 | ) 22 | 23 | public static let projectId = Lens( 24 | view: { $0.projectId }, 25 | set: { .init(description: $1.description, id: $1.id, name: $1.name, 26 | projectId: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/LocationLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Location { 4 | public enum lens { 5 | public static let country = Lens( 6 | view: { $0.country }, 7 | set: { Location(country: $0, displayableName: $1.displayableName, id: $1.id, name: $1.name) } 8 | ) 9 | 10 | public static let displayableName = Lens( 11 | view: { $0.displayableName }, 12 | set: { Location(country: $1.country, displayableName: $0, id: $1.id, name: $1.name) } 13 | ) 14 | 15 | public static let id = Lens( 16 | view: { $0.id }, 17 | set: { Location(country: $1.country, displayableName: $1.displayableName, id: $0, name: $1.name) } 18 | ) 19 | 20 | public static let name = Lens( 21 | view: { $0.name }, 22 | set: { Location(country: $1.country, displayableName: $1.displayableName, id: $1.id, name: $0) } 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /KsApi/models/lenses/MessageLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Message { 4 | public enum lens { 5 | public static let id = Lens( 6 | view: { $0.id }, 7 | set: { Message(body: $1.body, createdAt: $1.createdAt, id: $0, recipient: $1.recipient, 8 | sender: $1.sender) } 9 | ) 10 | 11 | public static let body = Lens( 12 | view: { $0.body }, 13 | set: { Message(body: $0, createdAt: $1.createdAt, id: $1.id, recipient: $1.recipient, 14 | sender: $1.sender) } 15 | ) 16 | 17 | public static let recipient = Lens( 18 | view: { $0.recipient }, 19 | set: { Message(body: $1.body, createdAt: $1.createdAt, id: $1.id, recipient: $0, 20 | sender: $1.sender) } 21 | ) 22 | 23 | public static let sender = Lens( 24 | view: { $0.sender }, 25 | set: { Message(body: $1.body, createdAt: $1.createdAt, id: $1.id, recipient: $1.recipient, 26 | sender: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/MessageThreadLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension MessageThread { 4 | public enum lens { 5 | public static let id = Lens( 6 | view: { $0.id }, 7 | set: { MessageThread(backing: $1.backing, closed: $1.closed, id: 0, lastMessage: $1.lastMessage, 8 | participant: $1.participant, project: $1.project, unreadMessagesCount: $1.unreadMessagesCount) } 9 | ) 10 | 11 | public static let participant = Lens( 12 | view: { $0.participant }, 13 | set: { MessageThread(backing: $1.backing, closed: $1.closed, id: $0.id, lastMessage: $1.lastMessage, 14 | participant: $0, project: $1.project, unreadMessagesCount: $1.unreadMessagesCount) } 15 | ) 16 | 17 | public static let project = Lens( 18 | view: { $0.project }, 19 | set: { MessageThread(backing: $1.backing, closed: $1.closed, id: $0.id, lastMessage: $1.lastMessage, 20 | participant: $1.participant, project: $0, unreadMessagesCount: $1.unreadMessagesCount) } 21 | ) 22 | 23 | public static let lastMessage = Lens( 24 | view: { $0.lastMessage }, 25 | set: { .init(backing: $1.backing, closed: $1.closed, id: $0.id, lastMessage: $0, 26 | participant: $1.participant, project: $1.project, 27 | unreadMessagesCount: $1.unreadMessagesCount) } 28 | ) 29 | 30 | public static let unreadMessagesCount = Lens( 31 | view: { $0.unreadMessagesCount }, 32 | set: { .init(backing: $1.backing, closed: $1.closed, id: $1.id, lastMessage: $1.lastMessage, 33 | participant: $1.participant, project: $1.project, unreadMessagesCount: $0) } 34 | ) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.CreatorDataLenses.swift: -------------------------------------------------------------------------------- 1 | // swiftlint:disable type_name 2 | import Prelude 3 | 4 | extension Project.CreatorData { 5 | public enum lens { 6 | public static let lastUpdatePublishedAt = Lens( 7 | view: { $0.lastUpdatePublishedAt }, 8 | set: { Project.CreatorData(lastUpdatePublishedAt: $0, permissions: $1.permissions, 9 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $1.unseenActivityCount) } 10 | ) 11 | 12 | public static let permissions = Lens( 13 | view: { $0.permissions }, 14 | set: { Project.CreatorData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $0, 15 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $1.unseenActivityCount) } 16 | ) 17 | 18 | public static let unreadMessagesCount = Lens( 19 | view: { $0.unreadMessagesCount }, 20 | set: { Project.CreatorData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $1.permissions, 21 | unreadMessagesCount: $0, unseenActivityCount: $1.unseenActivityCount) } 22 | ) 23 | 24 | public static let unseenActivityCount = Lens( 25 | view: { $0.unseenActivityCount }, 26 | set: { Project.CreatorData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $1.permissions, 27 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $0) } 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.DatesLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.Dates { 4 | public enum lens { 5 | public static let deadline = Lens( 6 | view: { $0.deadline }, 7 | set: { Project.Dates(deadline: $0, featuredAt: $1.featuredAt, launchedAt: $1.launchedAt, 8 | potdAt: $1.potdAt, stateChangedAt: $1.stateChangedAt) } 9 | ) 10 | 11 | public static let featuredAt = Lens( 12 | view: { $0.featuredAt }, 13 | set: { Project.Dates(deadline: $1.deadline, featuredAt: $0, launchedAt: $1.launchedAt, 14 | potdAt: $1.potdAt, stateChangedAt: $1.stateChangedAt) } 15 | ) 16 | 17 | public static let launchedAt = Lens( 18 | view: { $0.launchedAt }, 19 | set: { Project.Dates(deadline: $1.deadline, featuredAt: $1.featuredAt, launchedAt: $0, 20 | potdAt: $1.potdAt, stateChangedAt: $1.stateChangedAt) } 21 | ) 22 | 23 | public static let potdAt = Lens( 24 | view: { $0.potdAt }, 25 | set: { Project.Dates(deadline: $1.deadline, featuredAt: $1.featuredAt, launchedAt: $1.launchedAt, 26 | potdAt: $0, stateChangedAt: $1.stateChangedAt) } 27 | ) 28 | 29 | public static let stateChangedAt = Lens( 30 | view: { $0.stateChangedAt }, 31 | set: { Project.Dates(deadline: $1.deadline, featuredAt: $1.featuredAt, launchedAt: $1.launchedAt, 32 | potdAt: $1.potdAt, stateChangedAt: $0) } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.MemberDataLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.MemberData { 4 | public enum lens { 5 | public static let lastUpdatePublishedAt = Lens( 6 | view: { $0.lastUpdatePublishedAt }, 7 | set: { Project.MemberData(lastUpdatePublishedAt: $0, permissions: $1.permissions, 8 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $1.unseenActivityCount) } 9 | ) 10 | 11 | public static let permissions = Lens( 12 | view: { $0.permissions }, 13 | set: { Project.MemberData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $0, 14 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $1.unseenActivityCount) } 15 | ) 16 | 17 | public static let unreadMessagesCount = Lens( 18 | view: { $0.unreadMessagesCount }, 19 | set: { Project.MemberData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $1.permissions, 20 | unreadMessagesCount: $0, unseenActivityCount: $1.unseenActivityCount) } 21 | ) 22 | 23 | public static let unseenActivityCount = Lens( 24 | view: { $0.unseenActivityCount }, 25 | set: { Project.MemberData(lastUpdatePublishedAt: $1.lastUpdatePublishedAt, permissions: $1.permissions, 26 | unreadMessagesCount: $1.unreadMessagesCount, unseenActivityCount: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.PersonalizationLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.Personalization { 4 | public enum lens { 5 | public static let backing = Lens( 6 | view: { $0.backing }, 7 | set: { Project.Personalization(backing: $0, friends: $1.friends, isBacking: $1.isBacking, 8 | isStarred: $1.isStarred) } 9 | ) 10 | 11 | public static let friends = Lens( 12 | view: { $0.friends }, 13 | set: { Project.Personalization(backing: $1.backing, friends: $0, isBacking: $1.isBacking, 14 | isStarred: $1.isStarred) } 15 | ) 16 | 17 | public static let isBacking = Lens( 18 | view: { $0.isBacking }, 19 | set: { Project.Personalization(backing: $1.backing, friends: $1.friends, isBacking: $0, 20 | isStarred: $1.isStarred) } 21 | ) 22 | 23 | public static let isStarred = Lens( 24 | view: { $0.isStarred }, 25 | set: { Project.Personalization(backing: $1.backing, friends: $1.friends, isBacking: $1.isBacking, 26 | isStarred: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.PhotoLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.Photo { 4 | public enum lens { 5 | public static let full = Lens( 6 | view: { $0.full }, 7 | set: { .init(full: $0, med: $1.med, size1024x768: $1.size1024x768, small: $1.small) } 8 | ) 9 | 10 | public static let med = Lens( 11 | view: { $0.full }, 12 | set: { .init(full: $1.full, med: $0, size1024x768: $1.size1024x768, small: $1.small) } 13 | ) 14 | 15 | public static let size1024x768 = Lens( 16 | view: { $0.size1024x768 }, 17 | set: { .init(full: $1.full, med: $1.med, size1024x768: $0, small: $1.small) } 18 | ) 19 | 20 | public static let small = Lens( 21 | view: { $0.small }, 22 | set: { .init(full: $1.full, med: $1.med, size1024x768: $1.size1024x768, small: $0) } 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.StatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.Stats { 4 | public enum lens { 5 | public static let backersCount = Lens( 6 | view: { $0.backersCount }, 7 | set: { .init(backersCount: $0, commentsCount: $1.commentsCount, goal: $1.goal, 8 | pledged: $1.pledged, staticUsdRate: $1.staticUsdRate, updatesCount: $1.updatesCount) } 9 | ) 10 | 11 | public static let commentsCount = Lens( 12 | view: { $0.commentsCount }, 13 | set: { .init(backersCount: $1.backersCount, commentsCount: $0, goal: $1.goal, 14 | pledged: $1.pledged, staticUsdRate: $1.staticUsdRate, updatesCount: $1.updatesCount) } 15 | ) 16 | 17 | public static let goal = Lens( 18 | view: { $0.goal }, 19 | set: { .init(backersCount: $1.backersCount, commentsCount: $1.commentsCount, goal: $0, 20 | pledged: $1.pledged, staticUsdRate: $1.staticUsdRate, updatesCount: $1.updatesCount) } 21 | ) 22 | 23 | public static let pledged = Lens( 24 | view: { $0.pledged }, 25 | set: { .init(backersCount: $1.backersCount, commentsCount: $1.commentsCount, goal: $1.goal, 26 | pledged: $0, staticUsdRate: $1.staticUsdRate, updatesCount: $1.updatesCount) } 27 | ) 28 | 29 | public static let staticUsdRate = Lens( 30 | view: { $0.staticUsdRate }, 31 | set: { .init(backersCount: $1.backersCount, commentsCount: $1.commentsCount, goal: $1.goal, 32 | pledged: $1.pledged, staticUsdRate: $0, updatesCount: $1.updatesCount) } 33 | ) 34 | 35 | public static let updatesCount = Lens( 36 | view: { $0.updatesCount }, 37 | set: { .init(backersCount: $1.backersCount, commentsCount: $1.commentsCount, goal: $1.goal, 38 | pledged: $1.pledged, staticUsdRate: $1.staticUsdRate, updatesCount: $0) } 39 | ) 40 | 41 | public static let fundingProgress = Lens( 42 | view: { $0.fundingProgress }, 43 | set: { .init(backersCount: $1.backersCount, commentsCount: $1.commentsCount, goal: $1.goal, 44 | pledged: Int($0 * Float($1.goal)), staticUsdRate: $1.staticUsdRate, updatesCount: $1.updatesCount) } 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Project.VideoLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Project.Video { 4 | public enum lens { 5 | public static let id = Lens( 6 | view: { $0.id }, 7 | set: { .init(id: $0, high: $1.high) } 8 | ) 9 | 10 | public static let high = Lens( 11 | view: { $0.high }, 12 | set: { .init(id: $1.id, high: $0) } 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectNotification.ProjectLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectNotification.Project { 4 | public enum lens { 5 | public static let id = Lens ( 6 | view: { $0.id }, 7 | set: { ProjectNotification.Project(id: $0, name: $1.name) } 8 | ) 9 | 10 | public static let name = Lens ( 11 | view: { $0.name }, 12 | set: { ProjectNotification.Project(id: $1.id, name: $0) } 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectNotificationLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectNotification { 4 | public enum lens { 5 | public static let email = Lens( 6 | view: { $0.email }, 7 | set: { ProjectNotification(email: $0, id: $1.id, mobile: $1.mobile, project: $1.project) } 8 | ) 9 | 10 | public static let id = Lens( 11 | view: { $0.id }, 12 | set: { ProjectNotification(email: $1.email, id: $0, mobile: $1.mobile, project: $1.project) } 13 | ) 14 | 15 | public static let mobile = Lens( 16 | view: { $0.mobile }, 17 | set: { ProjectNotification(email: $1.email, id: $1.id, mobile: $0, project: $1.project) } 18 | ) 19 | 20 | public static let project = Lens( 21 | view: { $0.project }, 22 | set: { ProjectNotification(email: $1.email, id: $1.id, mobile: $1.mobile, project: $0) } 23 | ) 24 | } 25 | } 26 | 27 | extension Lens where Whole == ProjectNotification, Part == ProjectNotification.Project { 28 | public var id: Lens { 29 | return ProjectNotification.lens.project..ProjectNotification.Project.lens.id 30 | } 31 | 32 | public var name: Lens { 33 | return ProjectNotification.lens.project..ProjectNotification.Project.lens.name 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelope.CumulativeStatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope.CumulativeStats { 4 | public enum lens { 5 | public static let averagePledge = Lens( 6 | view: { $0.averagePledge }, 7 | set: { .init(averagePledge: $0, backersCount: $1.backersCount, goal: $1.goal, 8 | percentRaised: $1.percentRaised, pledged: $1.pledged) } 9 | ) 10 | 11 | public static let backersCount = Lens( 12 | view: { $0.backersCount }, 13 | set: { .init(averagePledge: $1.averagePledge, backersCount: $0, goal: $1.goal, 14 | percentRaised: $1.percentRaised, pledged: $1.pledged) } 15 | ) 16 | 17 | public static let goal = Lens( 18 | view: { $0.goal }, 19 | set: { .init(averagePledge: $1.averagePledge, backersCount: $1.backersCount, goal: $0, 20 | percentRaised: $1.percentRaised, pledged: $1.pledged) } 21 | ) 22 | 23 | public static let percentRaised = Lens( 24 | view: { $0.percentRaised }, 25 | set: { .init(averagePledge: $1.averagePledge, backersCount: $1.backersCount, goal: $1.goal, 26 | percentRaised: $0, pledged: $1.pledged) } 27 | ) 28 | 29 | public static let pledged = Lens( 30 | view: { $0.pledged }, 31 | set: { .init(averagePledge: $1.averagePledge, backersCount: $1.backersCount, goal: $1.goal, 32 | percentRaised: $1.percentRaised, pledged: $0) } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelope.FundingDateStatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope.FundingDateStats { 4 | public enum lens { 5 | public static let backersCount = Lens( 6 | view: { $0.backersCount }, 7 | set: { .init(backersCount: $0, cumulativePledged: $1.cumulativePledged, 8 | cumulativeBackersCount: $1.cumulativeBackersCount, date: $1.date, pledged: $1.pledged) } 9 | ) 10 | 11 | public static let cumulativePledged = Lens( 12 | view: { $0.cumulativePledged }, 13 | set: { .init(backersCount: $1.backersCount, cumulativePledged: $0, 14 | cumulativeBackersCount: $1.cumulativeBackersCount, date: $1.date, pledged: $1.pledged) } 15 | ) 16 | 17 | public static let cumulativeBackersCount = Lens( 18 | view: { $0.cumulativeBackersCount }, 19 | set: { .init(backersCount: $1.backersCount, cumulativePledged: $1.cumulativePledged, 20 | cumulativeBackersCount: $0, date: $1.date, pledged: $1.pledged) } 21 | ) 22 | 23 | public static let date = Lens( 24 | view: { $0.date }, 25 | set: { .init(backersCount: $1.backersCount, cumulativePledged: $1.cumulativePledged, 26 | cumulativeBackersCount: $1.cumulativeBackersCount, date: $0, pledged: $1.pledged) } 27 | ) 28 | 29 | public static let pledged = Lens( 30 | view: { $0.pledged }, 31 | set: { .init(backersCount: $1.backersCount, cumulativePledged: $1.cumulativePledged, 32 | cumulativeBackersCount: $1.cumulativeBackersCount, date: $1.date, pledged: $0) } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelope.ReferrerStatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope.ReferrerStats { 4 | public enum lens { 5 | public static let backersCount = Lens( 6 | view: { $0.backersCount }, 7 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $0, code: $1.code, 8 | percentageOfDollars: $1.percentageOfDollars, pledged: $1.pledged, referrerName: $1.referrerName, 9 | referrerType: $1.referrerType) } 10 | ) 11 | 12 | public static let code = Lens( 13 | view: { $0.code }, 14 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $1.backersCount, code: $0, 15 | percentageOfDollars: $1.percentageOfDollars, pledged: $1.pledged, referrerName: $1.referrerName, 16 | referrerType: $1.referrerType) } 17 | ) 18 | 19 | public static let percentageOfDollars = Lens( 20 | view: { $0.percentageOfDollars }, 21 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $1.backersCount, code: $1.code, 22 | percentageOfDollars: $0, pledged: $1.pledged, referrerName: $1.referrerName, 23 | referrerType: $1.referrerType) } 24 | ) 25 | 26 | public static let pledged = Lens( 27 | view: { $0.pledged }, 28 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $1.backersCount, code: $1.code, 29 | percentageOfDollars: $1.percentageOfDollars, pledged: $0, referrerName: $1.referrerName, 30 | referrerType: $1.referrerType) } 31 | ) 32 | 33 | public static let referrerName = Lens( 34 | view: { $0.referrerName }, 35 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $1.backersCount, code: $1.code, 36 | percentageOfDollars: $1.percentageOfDollars, pledged: $1.pledged, referrerName: $0, 37 | referrerType: $1.referrerType) } 38 | ) 39 | 40 | public static let referrerType = 41 | Lens( 42 | view: { $0.referrerType }, 43 | set: { ProjectStatsEnvelope.ReferrerStats(backersCount: $1.backersCount, code: $1.code, 44 | percentageOfDollars: $1.percentageOfDollars, pledged: $1.pledged, referrerName: $1.referrerName, 45 | referrerType: $0) } 46 | ) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelope.RewardDistributionLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope.RewardStats { 4 | public enum lens { 5 | public static let backersCount = Lens( 6 | view: { $0.backersCount }, 7 | set: { ProjectStatsEnvelope.RewardStats(backersCount: $0, rewardId: $1.rewardId, 8 | minimum: $1.minimum, pledged: $1.pledged) } 9 | ) 10 | 11 | public static let id = Lens( 12 | view: { $0.rewardId }, 13 | set: { ProjectStatsEnvelope.RewardStats(backersCount: $1.backersCount, rewardId: $0, 14 | minimum: $1.minimum, pledged: $1.pledged) } 15 | ) 16 | 17 | public static let minimum = Lens( 18 | view: { $0.minimum }, 19 | set: { ProjectStatsEnvelope.RewardStats(backersCount: $1.backersCount, 20 | rewardId: $1.rewardId, minimum: $0, pledged: $1.pledged) } 21 | ) 22 | 23 | public static let pledged = Lens( 24 | view: { $0.pledged }, 25 | set: { ProjectStatsEnvelope.RewardStats(backersCount: $1.backersCount, 26 | rewardId: $1.rewardId, minimum: $1.minimum, pledged: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelope.VideoStatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope.VideoStats { 4 | public enum lens { 5 | public static let externalCompletions = Lens( 6 | view: { $0.externalCompletions }, 7 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $0, externalStarts: $1.externalStarts, 8 | internalCompletions: $1.internalCompletions, internalStarts: $1.internalStarts) } 9 | ) 10 | 11 | public static let externalStarts = Lens( 12 | view: { $0.externalStarts }, 13 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, externalStarts: $0, 14 | internalCompletions: $1.internalCompletions, internalStarts: $1.internalStarts) } 15 | ) 16 | 17 | public static let internalCompletions = Lens( 18 | view: { $0.internalCompletions }, 19 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, 20 | externalStarts: $1.externalStarts, internalCompletions: $0, internalStarts: $1.internalStarts) } 21 | ) 22 | 23 | public static let internalStarts = Lens( 24 | view: { $0.internalStarts }, 25 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, 26 | externalStarts: $1.externalStarts, internalCompletions: $1.internalCompletions, internalStarts: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ProjectStatsEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ProjectStatsEnvelope { 4 | public enum lens { 5 | public static let cumulativeStats = Lens( 6 | view: { $0.cumulativeStats }, 7 | set: { ProjectStatsEnvelope(cumulativeStats: $0, fundingDistribution: $1.fundingDistribution, 8 | referralDistribution: $1.referralDistribution, rewardDistribution: $1.rewardDistribution, 9 | videoStats: $1.videoStats) } 10 | ) 11 | 12 | public static let fundingDistribution = 13 | Lens( 14 | view: { $0.fundingDistribution }, 15 | set: { ProjectStatsEnvelope(cumulativeStats: $1.cumulativeStats, fundingDistribution: $0, 16 | referralDistribution: $1.referralDistribution, rewardDistribution: $1.rewardDistribution, 17 | videoStats: $1.videoStats) } 18 | ) 19 | 20 | public static let referralDistribution = 21 | Lens( 22 | view: { $0.referralDistribution }, 23 | set: { ProjectStatsEnvelope(cumulativeStats: $1.cumulativeStats, 24 | fundingDistribution: $1.fundingDistribution, referralDistribution: $0, 25 | rewardDistribution: $1.rewardDistribution, videoStats: $1.videoStats) } 26 | ) 27 | 28 | public static let rewardDistribution = Lens( 29 | view: { $0.rewardDistribution }, 30 | set: { ProjectStatsEnvelope(cumulativeStats: $1.cumulativeStats, 31 | fundingDistribution: $1.fundingDistribution, referralDistribution: $1.referralDistribution, 32 | rewardDistribution: $0, videoStats: $1.videoStats) } 33 | ) 34 | 35 | public static let videoStats = Lens( 36 | view: { $0.videoStats }, 37 | set: { ProjectStatsEnvelope(cumulativeStats: $1.cumulativeStats, 38 | fundingDistribution: $1.fundingDistribution, referralDistribution: $1.referralDistribution, 39 | rewardDistribution: $1.rewardDistribution, videoStats: $0) } 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /KsApi/models/lenses/Reward.ShippingLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Reward.Shipping { 4 | public enum lens { 5 | public static let enabled = Lens( 6 | view: { $0.enabled }, 7 | set: { .init(enabled: $0, preference: $1.preference, summary: $1.summary) } 8 | ) 9 | 10 | public static let preference = Lens( 11 | view: { $0.preference }, 12 | set: { .init(enabled: $1.enabled, preference: $0, summary: $1.summary) } 13 | ) 14 | 15 | public static let summary = Lens( 16 | view: { $0.summary }, 17 | set: { .init(enabled: $1.enabled, preference: $1.preference, summary: $0) } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/RewardItemLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension RewardsItem { 4 | public enum lens { 5 | public static let id = Lens( 6 | view: { $0.id }, 7 | set: { .init(id: $0, item: $1.item, quantity: $1.quantity, rewardId: $1.rewardId) } 8 | ) 9 | 10 | public static let item = Lens( 11 | view: { $0.item }, 12 | set: { .init(id: $1.id, item: $0, quantity: $1.quantity, rewardId: $1.rewardId) } 13 | ) 14 | 15 | public static let quantity = Lens( 16 | view: { $0.quantity }, 17 | set: { .init(id: $1.id, item: $1.item, quantity: $0, rewardId: $1.rewardId) } 18 | ) 19 | 20 | public static let rewardId = Lens( 21 | view: { $0.rewardId }, 22 | set: { .init(id: $1.id, item: $1.item, quantity: $1.quantity, rewardId: $0) } 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ShippingRuleLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ShippingRule { 4 | public enum lens { 5 | public static let cost = Lens( 6 | view: { $0.cost }, 7 | set: { .init(cost: $0, id: $1.id, location: $1.location) } 8 | ) 9 | 10 | public static let id = Lens( 11 | view: { $0.id }, 12 | set: { .init(cost: $1.cost, id: $0, location: $1.location) } 13 | ) 14 | 15 | public static let location = Lens( 16 | view: { $0.location }, 17 | set: { .init(cost: $1.cost, id: $1.id, location: $0) } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/ShippingRulesEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension ShippingRulesEnvelope { 4 | public enum lens { 5 | public static let shippingRules = Lens( 6 | view: { $0.shippingRules }, 7 | set: { shippingRules, _ in .init(shippingRules: shippingRules) } 8 | ) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/lenses/StarEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension StarEnvelope { 4 | public enum lens { 5 | public static let user = Lens( 6 | view: { $0.user }, 7 | set: { StarEnvelope(user: $0, project: $1.project) } 8 | ) 9 | 10 | public static let project = Lens( 11 | view: { $0.project }, 12 | set: { StarEnvelope(user: $1.user, project: $0) } 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/lenses/SubmitApplePayEnvelopeLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension SubmitApplePayEnvelope { 4 | public enum lens { 5 | public static let thankYouUrl = Lens( 6 | view: { $0.thankYouUrl }, 7 | set: { .init(thankYouUrl: $0, status: $1.status) } 8 | ) 9 | 10 | public static let status = Lens( 11 | view: { $0.status }, 12 | set: { .init(thankYouUrl: $1.thankYouUrl, status: $0) } 13 | ) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/lenses/SurveyResponseLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension SurveyResponse { 4 | public enum lens { 5 | public static let answeredAt = Lens( 6 | view: { $0.answeredAt }, 7 | set: { .init(answeredAt: $0, id: $1.id, project: $1.project, urls: $1.urls) } 8 | ) 9 | 10 | public static let id = Lens( 11 | view: { $0.id }, 12 | set: { .init(answeredAt: $1.answeredAt, id: $0, project: $1.project, urls: $1.urls) } 13 | ) 14 | 15 | public static let project = Lens( 16 | view: { $0.project }, 17 | set: { .init(answeredAt: $1.answeredAt, id: $1.id, project: $0, urls: $1.urls) } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/UpdateDraftLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension UpdateDraft { 4 | public enum lens { 5 | public static let update = Lens( 6 | view: { $0.update }, 7 | set: { UpdateDraft(update: $0, images: $1.images, video: $1.video) } 8 | ) 9 | 10 | public static let images = Lens( 11 | view: { $0.images }, 12 | set: { UpdateDraft(update: $1.update, images: $0, video: $1.video) } 13 | ) 14 | 15 | public static let video = Lens( 16 | view: { $0.video }, 17 | set: { UpdateDraft(update: $1.update, images: $1.images, video: $0) } 18 | ) 19 | } 20 | } 21 | 22 | extension UpdateDraft.Image { 23 | public enum lens { 24 | public static let id = Lens( 25 | view: { $0.id }, 26 | set: { UpdateDraft.Image(id: $0, thumb: $1.thumb, full: $1.full) } 27 | ) 28 | 29 | public static let thumb = Lens( 30 | view: { $0.thumb }, 31 | set: { UpdateDraft.Image(id: $1.id, thumb: $0, full: $1.full) } 32 | ) 33 | 34 | public static let full = Lens( 35 | view: { $0.thumb }, 36 | set: { UpdateDraft.Image(id: $1.id, thumb: $1.thumb, full: $0) } 37 | ) 38 | } 39 | } 40 | 41 | extension UpdateDraft.Video { 42 | public enum lens { 43 | public static let id = Lens( 44 | view: { $0.id }, 45 | set: { UpdateDraft.Video(id: $0, status: $1.status, frame: $1.frame) } 46 | ) 47 | 48 | public static let status = Lens( 49 | view: { $0.status }, 50 | set: { UpdateDraft.Video(id: $1.id, status: $0, frame: $1.frame) } 51 | ) 52 | 53 | public static let frame = Lens( 54 | view: { $0.frame }, 55 | set: { UpdateDraft.Video(id: $1.id, status: $1.status, frame: $0) } 56 | ) 57 | } 58 | } 59 | 60 | extension Lens where Whole == UpdateDraft, Part == Update { 61 | public var id: Lens { 62 | return UpdateDraft.lens.update..Update.lens.id 63 | } 64 | 65 | public var projectId: Lens { 66 | return UpdateDraft.lens.update..Update.lens.projectId 67 | } 68 | 69 | public var title: Lens { 70 | return UpdateDraft.lens.update..Update.lens.title 71 | } 72 | 73 | public var body: Lens { 74 | return UpdateDraft.lens.update..Update.lens.body 75 | } 76 | 77 | public var isPublic: Lens { 78 | return UpdateDraft.lens.update..Update.lens.isPublic 79 | } 80 | 81 | public var sequence: Lens { 82 | return UpdateDraft.lens.update..Update.lens.sequence 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /KsApi/models/lenses/User.AvatarLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension User.Avatar { 4 | public enum lens { 5 | public static let large = Lens( 6 | view: { $0.large }, 7 | set: { User.Avatar(large: $0, medium: $1.medium, small: $1.small) } 8 | ) 9 | 10 | public static let medium = Lens( 11 | view: { $0.medium }, 12 | set: { User.Avatar(large: $1.large, medium: $0, small: $1.small) } 13 | ) 14 | 15 | public static let small = Lens( 16 | view: { $0.small }, 17 | set: { User.Avatar(large: $1.large, medium: $1.medium, small: $0) } 18 | ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/lenses/User.NewsletterSubscriptionsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension User.NewsletterSubscriptions { 4 | public enum lens { 5 | public static let games = Lens( 6 | view: { $0.games }, 7 | set: { User.NewsletterSubscriptions(games: $0, happening: $1.happening, promo: $1.promo, 8 | weekly: $1.weekly) } 9 | ) 10 | 11 | public static let happening = Lens( 12 | view: { $0.happening }, 13 | set: { User.NewsletterSubscriptions(games: $1.games, happening: $0, promo: $1.promo, 14 | weekly: $1.weekly) } 15 | ) 16 | 17 | public static let promo = Lens( 18 | view: { $0.promo }, 19 | set: { User.NewsletterSubscriptions(games: $1.games, happening: $1.happening, promo: $0, 20 | weekly: $1.weekly) } 21 | ) 22 | 23 | public static let weekly = Lens( 24 | view: { $0.weekly }, 25 | set: { User.NewsletterSubscriptions(games: $1.games, happening: $1.happening, promo: $1.promo, 26 | weekly: $0) } 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /KsApi/models/lenses/User.StatsLenses.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension User.Stats { 4 | public enum lens { 5 | public static let backedProjectsCount = Lens( 6 | view: { $0.backedProjectsCount }, 7 | set: { User.Stats(backedProjectsCount: $0, createdProjectsCount: $1.createdProjectsCount, 8 | memberProjectsCount: $1.memberProjectsCount, starredProjectsCount: $1.starredProjectsCount, 9 | unansweredSurveysCount: $1.unansweredSurveysCount, unreadMessagesCount: $1.unreadMessagesCount) } 10 | ) 11 | 12 | public static let createdProjectsCount = Lens( 13 | view: { $0.createdProjectsCount }, 14 | set: { User.Stats(backedProjectsCount: $1.backedProjectsCount, createdProjectsCount: $0, 15 | memberProjectsCount: $1.memberProjectsCount, starredProjectsCount: $1.starredProjectsCount, 16 | unansweredSurveysCount: $1.unansweredSurveysCount, unreadMessagesCount: $1.unreadMessagesCount) } 17 | ) 18 | 19 | public static let memberProjectsCount = Lens( 20 | view: { $0.memberProjectsCount }, 21 | set: { User.Stats(backedProjectsCount: $1.backedProjectsCount, 22 | createdProjectsCount: $1.createdProjectsCount, memberProjectsCount: $0, 23 | starredProjectsCount: $1.starredProjectsCount, unansweredSurveysCount: $1.unansweredSurveysCount, 24 | unreadMessagesCount: $1.unreadMessagesCount) } 25 | ) 26 | 27 | public static let starredProjectsCount = Lens( 28 | view: { $0.starredProjectsCount }, 29 | set: { User.Stats(backedProjectsCount: $1.backedProjectsCount, 30 | createdProjectsCount: $1.createdProjectsCount, memberProjectsCount: $1.memberProjectsCount, 31 | starredProjectsCount: $0, unansweredSurveysCount: $1.unansweredSurveysCount, 32 | unreadMessagesCount: $1.unreadMessagesCount) } 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /KsApi/models/lenses/VideoStatsLenses.swift: -------------------------------------------------------------------------------- 1 | // swiftlint:disable type_name 2 | import Prelude 3 | 4 | extension ProjectStatsEnvelope.VideoStats { 5 | public enum lens { 6 | public static let externalCompletions = Lens( 7 | view: { $0.externalCompletions }, 8 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $0, externalStarts: $1.externalStarts, 9 | internalCompletions: $1.internalCompletions, internalStarts: $1.internalStarts) } 10 | ) 11 | 12 | public static let externalStarts = Lens( 13 | view: { $0.externalStarts }, 14 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, externalStarts: $0, 15 | internalCompletions: $1.internalCompletions, internalStarts: $1.internalStarts) } 16 | ) 17 | 18 | public static let internalCompletions = Lens( 19 | view: { $0.internalCompletions }, 20 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, 21 | externalStarts: $1.externalStarts, internalCompletions: $0, internalStarts: $1.internalStarts) } 22 | ) 23 | 24 | public static let internalStarts = Lens( 25 | view: { $0.internalStarts }, 26 | set: { ProjectStatsEnvelope.VideoStats(externalCompletions: $1.externalCompletions, 27 | externalStarts: $1.externalStarts, internalCompletions: $1.internalCompletions, internalStarts: $0) } 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /KsApi/models/templates/ActivityTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Activity { 2 | internal static let template = Activity( 3 | category: .launch, 4 | comment: nil, 5 | createdAt: Date(timeIntervalSince1970: 1475361315).timeIntervalSince1970, 6 | id: 1, 7 | memberData: Activity.MemberData( 8 | amount: nil, 9 | backing: nil, 10 | oldAmount: nil, 11 | oldRewardId: nil, 12 | newAmount: nil, 13 | newRewardId: nil, 14 | rewardId: nil 15 | ), 16 | project: .template, 17 | update: nil, 18 | user: .template 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/templates/BackingTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Backing { 2 | internal static let template = Backing( 3 | amount: 10, 4 | backer: .template, 5 | backerId: 1, 6 | id: 1, 7 | locationId: 1, 8 | pledgedAt: Date(timeIntervalSince1970: 1475361315).timeIntervalSince1970, 9 | projectCountry: "US", 10 | projectId: 1, 11 | reward: .template, 12 | rewardId: 1, 13 | sequence: 10, 14 | shippingAmount: 2, 15 | status: .pledged 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /KsApi/models/templates/CategoriesEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension CategoriesEnvelope { 2 | internal static let template = CategoriesEnvelope( 3 | categories: [ 4 | .art, 5 | .filmAndVideo, 6 | .illustration, 7 | .documentary 8 | ] 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/templates/CategoryTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension KsApi.Category { 4 | internal static let template = Category( 5 | color: nil, 6 | id: 1, 7 | name: "Art", 8 | parent: nil, 9 | parentId: nil, 10 | position: 1, 11 | projectsCount: 450, 12 | slug: "art" 13 | ) 14 | 15 | internal static let art = template 16 | |> Category.lens.id .~ 1 17 | <> Category.lens.name .~ "Art" 18 | <> Category.lens.slug .~ "art" 19 | <> Category.lens.position .~ 1 20 | 21 | internal static let filmAndVideo = template 22 | |> Category.lens.id .~ 11 23 | <> Category.lens.name .~ "Film & Video" 24 | <> Category.lens.slug .~ "film-and-video" 25 | <> Category.lens.position .~ 7 26 | 27 | internal static let games = template 28 | |> Category.lens.id .~ 12 29 | <> Category.lens.name .~ "Games" 30 | <> Category.lens.slug .~ "games" 31 | <> Category.lens.position .~ 9 32 | 33 | internal static let illustration = template 34 | |> Category.lens.id .~ 22 35 | <> Category.lens.name .~ "Illustration" 36 | <> Category.lens.slug .~ "art/illustration" 37 | <> Category.lens.position .~ 4 38 | <> Category.lens.parentId .~ Category.art.id 39 | <> Category.lens.parent .~ Category.art 40 | 41 | internal static let documentary = template 42 | |> Category.lens.id .~ 30 43 | <> Category.lens.name .~ "Documentary" 44 | <> Category.lens.slug .~ "film-and-video/documentary" 45 | <> Category.lens.position .~ 4 46 | <> Category.lens.parentId .~ Category.filmAndVideo.id 47 | <> Category.lens.parent .~ Category.filmAndVideo 48 | 49 | internal static let tabletopGames = template 50 | |> Category.lens.id .~ 34 51 | <> Category.lens.name .~ "Tabletop Games" 52 | <> Category.lens.slug .~ "games/tabletop-games" 53 | <> Category.lens.position .~ 9 54 | <> Category.lens.parentId .~ Category.games.id 55 | <> Category.lens.parent .~ Category.games 56 | } 57 | -------------------------------------------------------------------------------- /KsApi/models/templates/ChangePaymentMethodEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ChangePaymentMethodEnvelope { 2 | internal static let template = ChangePaymentMethodEnvelope( 3 | newCheckoutUrl: "checkouts/1/payments/new", 4 | status: 200 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /KsApi/models/templates/CheckoutEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension CheckoutEnvelope { 2 | internal static let template = CheckoutEnvelope( 3 | state: .authorizing, 4 | stateReason: "" 5 | ) 6 | 7 | internal static let authorizing = template 8 | 9 | internal static let failed = CheckoutEnvelope( 10 | state: .failed, 11 | stateReason: "Sorry, something went wrong." 12 | ) 13 | 14 | internal static let successful = CheckoutEnvelope( 15 | state: .successful, 16 | stateReason: "" 17 | ) 18 | 19 | internal static let verifying = CheckoutEnvelope( 20 | state: .verifying, 21 | stateReason: "Blob, your payment method change is being processed." 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /KsApi/models/templates/CommentTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Comment { 2 | internal static let template = Comment( 3 | author: .template, 4 | body: "Exciting!", 5 | createdAt: Date(timeIntervalSince1970: 1475361315).timeIntervalSince1970, 6 | deletedAt: nil, 7 | id: 1 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /KsApi/models/templates/CommentsEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension CommentsEnvelope { 2 | internal static let template = CommentsEnvelope( 3 | comments: [Comment.template], 4 | urls: CommentsEnvelope.UrlsEnvelope( 5 | api: CommentsEnvelope.UrlsEnvelope.ApiEnvelope( 6 | moreComments: "" 7 | ) 8 | ) 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/templates/ConfigTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Config { 4 | internal static let template = Config( 5 | abExperiments: [:], 6 | appId: 123456789, 7 | applePayCountries: ["US", "GB", "CA", "AU", "FR", "CH", "SG", "HK", "ES", "NZ"], 8 | countryCode: "US", 9 | features: [:], 10 | iTunesLink: "http://www.itunes.com", 11 | launchedCountries: [.US, .CA, .AU, .NZ, .GB, .NL, .IE, .DE, .ES, .FR, .IT, .AT, .BE, .LU, .SE, .DK, .NO, 12 | .CH, .HK, .SG], 13 | locale: "en", 14 | stripePublishableKey: "pk" 15 | ) 16 | 17 | internal static let config = Config.template 18 | 19 | internal static let deConfig = Config.template 20 | |> Config.lens.countryCode .~ "DE" 21 | |> Config.lens.locale .~ "de" 22 | } 23 | -------------------------------------------------------------------------------- /KsApi/models/templates/CreatePledgeEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension CreatePledgeEnvelope { 2 | internal static let template = CreatePledgeEnvelope( 3 | checkoutUrl: "checkouts/1/payments", 4 | newCheckoutUrl: "checkouts/1/payments/new", 5 | status: 200 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /KsApi/models/templates/DiscoveryEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension DiscoveryEnvelope { 4 | internal static let template = DiscoveryEnvelope( 5 | projects: [.template], 6 | urls: .template, 7 | stats: .template 8 | ) 9 | } 10 | 11 | extension DiscoveryEnvelope.UrlsEnvelope { 12 | internal static let template = DiscoveryEnvelope.UrlsEnvelope( 13 | api: .template 14 | ) 15 | } 16 | 17 | extension DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope { 18 | internal static let template = DiscoveryEnvelope.UrlsEnvelope.ApiEnvelope( 19 | more_projects: "http://\(Secrets.Api.Endpoint.production)/gimme/more" 20 | ) 21 | } 22 | 23 | extension DiscoveryEnvelope.StatsEnvelope { 24 | internal static let template = DiscoveryEnvelope.StatsEnvelope( 25 | count: 200 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /KsApi/models/templates/FindFriendsEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension FindFriendsEnvelope { 4 | internal static let template = FindFriendsEnvelope( 5 | contactsImported: true, 6 | urls: FindFriendsEnvelope.UrlsEnvelope( 7 | api: FindFriendsEnvelope.UrlsEnvelope.ApiEnvelope( 8 | moreUsers: "http://somelink.com/more" 9 | ) 10 | ), 11 | users: (1...3).map { User.template |> User.lens.id .~ $0 } 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /KsApi/models/templates/FriendStatsEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension FriendStatsEnvelope { 2 | internal static let template = FriendStatsEnvelope( 3 | stats: FriendStatsEnvelope.Stats( 4 | friendProjectsCount: 100, 5 | remoteFriendsCount: 100 6 | ) 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/ItemTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Item { 2 | internal static let template = Item( 3 | description: "This is an item.", 4 | id: 1, 5 | name: "The Item", 6 | projectId: 1 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/LocationTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Location { 2 | internal static let template = Location(country: "US", 3 | displayableName: "Brooklyn, NY", 4 | id: 42, 5 | name: "Brooklyn") 6 | 7 | internal static let brooklyn = Location(country: "US", 8 | displayableName: "Brooklyn, NY", 9 | id: 1, 10 | name: "Brooklyn") 11 | 12 | internal static let losAngeles = Location(country: "US", 13 | displayableName: "Los Angeles, CA", 14 | id: 2, 15 | name: "Los Angeles") 16 | 17 | internal static let portland = Location(country: "US", 18 | displayableName: "Portland, OR", 19 | id: 3, 20 | name: "Portland") 21 | 22 | internal static let london = Location(country: "GB", 23 | displayableName: "London, GB", 24 | id: 4, 25 | name: "London") 26 | 27 | internal static let usa = Location(country: "US", 28 | displayableName: "United States", 29 | id: 5, 30 | name: "United States") 31 | 32 | internal static let canada = Location(country: "CA", 33 | displayableName: "Canada", 34 | id: 6, 35 | name: "Canada") 36 | 37 | internal static let greatBritain = Location(country: "GB", 38 | displayableName: "Great Britain", 39 | id: 7, 40 | name: "Great Britain") 41 | 42 | internal static let australia = Location(country: "AU", 43 | displayableName: "Australia", 44 | id: 8, 45 | name: "Australia") 46 | } 47 | -------------------------------------------------------------------------------- /KsApi/models/templates/MessageTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Message { 4 | internal static let template = Message( 5 | body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam augue dolor, " + 6 | "accumsan nec aliquam a, porttitor sed dui. Integer iaculis ipsum fringilla metus " + 7 | "porttitor euismod. Donec in libero vitae lectus ultrices vehicula id eget dolor. " + 8 | "Nulla lacinia erat a ullamcorper sollicitudin.", 9 | createdAt: Date(timeIntervalSince1970: 1475361315).timeIntervalSince1970, 10 | id: 1, 11 | recipient: .template, 12 | sender: .template |> User.lens.id %~ { $0 + 1 } 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /KsApi/models/templates/MessageThreadTemplates.swift: -------------------------------------------------------------------------------- 1 | extension MessageThread { 2 | internal static let template = MessageThread( 3 | backing: nil, 4 | closed: false, 5 | id: 1, 6 | lastMessage: .template, 7 | participant: .template, 8 | project: .template, 9 | unreadMessagesCount: 1 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /KsApi/models/templates/Project.PhotoTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Project.Photo { 2 | internal static let template = Project.Photo( 3 | full: "http://www.kickstarter.com/full.jpg", 4 | med: "http://www.kickstarter.com/med.jpg", 5 | size1024x768: "http://www.kickstarter.com/1024x768.jpg", 6 | small: "http://www.kickstarter.com/small.jpg" 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/Project.VideoTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Project.Video { 2 | internal static let template = Project.Video( 3 | id: 1, 4 | high: "http://www.kickstarter.com/video.mp4" 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectNotificationTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectNotification { 2 | internal static let template = ProjectNotification( 3 | email: false, 4 | id: 1, 5 | mobile: false, 6 | project: ProjectNotification.Project( 7 | id: 1738, 8 | name: "The Project" 9 | ) 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelope.CumulativeStatsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope.CumulativeStats { 2 | internal static let template = ProjectStatsEnvelope.CumulativeStats( 3 | averagePledge: 0, 4 | backersCount: 0, 5 | goal: 0, 6 | percentRaised: 0, 7 | pledged: 0 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelope.FundingDateStatsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope.FundingDateStats { 2 | internal static let template = 3 | ProjectStatsEnvelope.FundingDateStats( 4 | backersCount: 0, 5 | cumulativePledged: 0, 6 | cumulativeBackersCount: 0, 7 | date: 0, 8 | pledged: 0 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelope.ReferrerStatsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope.ReferrerStats { 2 | internal static let template = ProjectStatsEnvelope.ReferrerStats( 3 | backersCount: 0, 4 | code: "", 5 | percentageOfDollars: 0.0, 6 | pledged: 0, 7 | referrerName: "", 8 | referrerType: .`internal` 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelope.RewardDistributionTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope.RewardStats { 2 | internal static let template = ProjectStatsEnvelope.RewardStats( 3 | backersCount: 50, 4 | rewardId: 400, 5 | minimum: 10, 6 | pledged: 500 7 | ) 8 | 9 | internal static let unPledged = ProjectStatsEnvelope.RewardStats( 10 | backersCount: 0, 11 | rewardId: Reward.noReward.id, 12 | minimum: 1, 13 | pledged: 0 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelope.VideoStatsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope.VideoStats { 2 | internal static let template = ProjectStatsEnvelope.VideoStats( 3 | externalCompletions: 5, 4 | externalStarts: 5, 5 | internalCompletions: 5, 6 | internalStarts: 5 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/ProjectStatsEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ProjectStatsEnvelope { 2 | internal static let template = ProjectStatsEnvelope( 3 | // using `.template` causes a segfault in release builds 4 | cumulativeStats: ProjectStatsEnvelope.CumulativeStats( 5 | averagePledge: 0, 6 | backersCount: 0, 7 | goal: 0, 8 | percentRaised: 0, 9 | pledged: 0 10 | ), 11 | fundingDistribution: [.template], 12 | referralDistribution: [.template], 13 | rewardDistribution: [.template], 14 | videoStats: .template 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /KsApi/models/templates/RewardTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension Reward { 4 | internal static let template = Reward( 5 | backersCount: 50, 6 | description: "A cool thing", 7 | endsAt: nil, 8 | estimatedDeliveryOn: Date( 9 | timeIntervalSince1970: 1475361315).timeIntervalSince1970 + 60.0 * 60.0 * 24.0 * 365.0, 10 | id: 1, 11 | limit: 100, 12 | minimum: 10, 13 | remaining: 50, 14 | rewardsItems: [], 15 | shipping: Reward.Shipping( 16 | enabled: false, 17 | preference: nil, 18 | summary: nil 19 | ), 20 | startsAt: nil, 21 | title: nil 22 | ) 23 | 24 | public static let noReward = Reward( 25 | backersCount: nil, 26 | description: "", 27 | endsAt: nil, 28 | estimatedDeliveryOn: nil, 29 | id: 0, 30 | limit: nil, 31 | minimum: 0, 32 | remaining: nil, 33 | rewardsItems: [], 34 | shipping: Reward.Shipping(enabled: false, preference: nil, summary: nil 35 | ), 36 | startsAt: nil, 37 | title: nil 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /KsApi/models/templates/RewardsItemTemplates.swift: -------------------------------------------------------------------------------- 1 | extension RewardsItem { 2 | internal static let template = RewardsItem( 3 | id: 1, 4 | item: Item.template, 5 | quantity: 1, 6 | rewardId: 1 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/ShippingRuleTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ShippingRule { 2 | public static let template = ShippingRule(cost: 5.0, id: 1, location: .template) 3 | } 4 | -------------------------------------------------------------------------------- /KsApi/models/templates/ShippingRulesEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension ShippingRulesEnvelope { 2 | internal static let template = ShippingRulesEnvelope(shippingRules: []) 3 | } 4 | -------------------------------------------------------------------------------- /KsApi/models/templates/StarEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension StarEnvelope { 4 | internal static let template = StarEnvelope( 5 | user: .template, 6 | project: .template 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/SubmitApplePayTemplates.swift: -------------------------------------------------------------------------------- 1 | extension SubmitApplePayEnvelope { 2 | internal static let template = SubmitApplePayEnvelope( 3 | thankYouUrl: "https://www.kickstarter.com/thanks", 4 | status: 200 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /KsApi/models/templates/SurveyResponseTemplates.swift: -------------------------------------------------------------------------------- 1 | extension SurveyResponse { 2 | internal static let template = SurveyResponse( 3 | answeredAt: nil, 4 | id: 1, 5 | project: .template, 6 | urls: SurveyResponse.UrlsEnvelope( 7 | web: SurveyResponse.UrlsEnvelope.WebEnvelope( 8 | survey: "https://www.kickstarter.com/projects/creator/project/surveys/1" 9 | ) 10 | ) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /KsApi/models/templates/UpdateDraftTemplates.swift: -------------------------------------------------------------------------------- 1 | import Prelude 2 | 3 | extension UpdateDraft { 4 | internal static let template = UpdateDraft( 5 | update: .template, 6 | images: [], 7 | video: nil 8 | ) 9 | 10 | internal static let blank = template 11 | |> UpdateDraft.lens.update.title .~ "" 12 | |> UpdateDraft.lens.update.body .~ "" 13 | |> UpdateDraft.lens.update.isPublic .~ true 14 | } 15 | 16 | extension UpdateDraft.Image { 17 | internal static let template = UpdateDraft.Image( 18 | id: 1, 19 | thumb: "test-thumb.png", 20 | full: "test-full.png" 21 | ) 22 | } 23 | 24 | extension UpdateDraft.Video { 25 | internal static let template = UpdateDraft.Video( 26 | id: 1, 27 | status: .successful, 28 | frame: "test-frame.png" 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /KsApi/models/templates/UpdatePledgeEnvelopeTemplates.swift: -------------------------------------------------------------------------------- 1 | extension UpdatePledgeEnvelope { 2 | internal static let template = UpdatePledgeEnvelope( 3 | newCheckoutUrl: "checkouts/1/payments/new", 4 | status: 200 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /KsApi/models/templates/UpdateTemplates.swift: -------------------------------------------------------------------------------- 1 | // swiftlint:disable line_length 2 | extension Update { 3 | internal static let template = Update( 4 | body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam id vulputate augue. Donec elementum est facilisis dolor accumsan feugiat. Nam et pellentesque massa. Sed sit amet commodo ligula. Sed viverra, est viverra pretium luctus, arcu ligula congue neque, sed bibendum neque quam vel elit. Nunc varius orci et tempus consequat. Nullam tempor velit vitae consectetur mattis. Proin dignissim id turpis ac fermentum.", 5 | commentsCount: 2, 6 | hasLiked: false, 7 | id: 1, 8 | isPublic: true, 9 | likesCount: 3, 10 | projectId: 1, 11 | publishedAt: Date(timeIntervalSince1970: 1475361315).timeIntervalSince1970, 12 | sequence: 1, 13 | title: "Hello", 14 | urls: Update.UrlsEnvelope(web: Update.UrlsEnvelope.WebEnvelope( 15 | update: "https://www.kickstarter.com/projects/udoo/udoo-x86/posts/1571540") 16 | ), 17 | user: nil, 18 | visible: true 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /KsApi/models/templates/User.AvatarTemplates.swift: -------------------------------------------------------------------------------- 1 | extension User.Avatar { 2 | internal static let template = User.Avatar( 3 | large: "http://www.kickstarter.com/large.jpg", 4 | medium: "http://www.kickstarter.com/medium.jpg", 5 | small: "http://www.kickstarter.com/small.jpg" 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /KsApi/models/templates/User.MemberDataTemplates.swift: -------------------------------------------------------------------------------- 1 | extension Project.MemberData { 2 | internal static let template = Project.MemberData( 3 | lastUpdatePublishedAt: nil, 4 | permissions: [], 5 | unreadMessagesCount: 0, 6 | unseenActivityCount: 0 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/User.NewsletterSubscriptionsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension User.NewsletterSubscriptions { 2 | internal static let template = User.NewsletterSubscriptions( 3 | games: false, 4 | happening: false, 5 | promo: false, 6 | weekly: false 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /KsApi/models/templates/User.NotificationsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension User.Notifications { 2 | internal static let template = User.Notifications( 3 | backings: false, 4 | comments: false, 5 | follower: false, 6 | friendActivity: false, 7 | mobileBackings: false, 8 | mobileComments: false, 9 | mobileFollower: false, 10 | mobileFriendActivity: false, 11 | mobilePostLikes: false, 12 | mobileUpdates: false, 13 | postLikes: false, 14 | updates: false 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /KsApi/models/templates/User.StatsTemplates.swift: -------------------------------------------------------------------------------- 1 | extension User.Stats { 2 | internal static let template = User.Stats( 3 | backedProjectsCount: nil, 4 | createdProjectsCount: nil, 5 | memberProjectsCount: nil, 6 | starredProjectsCount: nil, 7 | unansweredSurveysCount: nil, 8 | unreadMessagesCount: nil 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /KsApi/models/templates/UserTemplates.swift: -------------------------------------------------------------------------------- 1 | // swiftlint:disable line_length 2 | import Prelude 3 | 4 | extension User { 5 | internal static let template = User( 6 | avatar: .template, 7 | facebookConnected: nil, 8 | id: 1, 9 | isFriend: nil, 10 | liveAuthToken: "deadbeef", 11 | location: nil, 12 | name: "Blob", 13 | newsletters: .template, 14 | notifications: .template, 15 | social: nil, 16 | stats: .template 17 | ) 18 | 19 | internal static let brando = .template 20 | |> User.lens.avatar.large .~ "https://ksr-ugc.imgix.net/assets/006/258/518/b9033f46095b83119188cf9a66d19356_original.jpg?w=160&h=160&fit=crop&v=1461376829&auto=format&q=92&s=8d7666f01ab6765c3cf09149751ff077" 21 | |> User.lens.avatar.medium .~ "https://ksr-ugc.imgix.net/assets/006/258/518/b9033f46095b83119188cf9a66d19356_original.jpg?w=40&h=40&fit=crop&v=1461376829&auto=format&q=92&s=0fcedf8888ca6990408ccde81888899b" 22 | |> User.lens.avatar.small .~ "https://ksr-ugc.imgix.net/assets/006/258/518/b9033f46095b83119188cf9a66d19356_original.jpg?w=40&h=40&fit=crop&v=1461376829&auto=format&q=92&s=0fcedf8888ca6990408ccde81888899b" 23 | |> User.lens.id .~ "brando".hash 24 | |> User.lens.name .~ "Brandon Williams" 25 | } 26 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016 Kickstarter, PBC 2 | 3 | # Argo 4 | 5 | Functional JSON parsing library for Swift https://thoughtbot.com/open-source 6 | 7 | https://github.com/thoughtbot/Argo 8 | 9 | Argo is Copyright (c) 2015 thoughtbot, inc. 10 | 11 | Copyright (c) 2014 thoughtbot, inc. 12 | 13 | MIT License 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining 16 | a copy of this software and associated documentation files (the 17 | "Software"), to deal in the Software without restriction, including 18 | without limitation the rights to use, copy, modify, merge, publish, 19 | distribute, sublicense, and/or sell copies of the Software, and to 20 | permit persons to whom the Software is furnished to do so, subject to 21 | the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be 24 | included in all copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 30 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 31 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | 34 | # ReactiveCocoa 35 | 36 | Streams of values over time 37 | 38 | https://github.com/ReactiveCocoa/ReactiveCocoa 39 | 40 | **Copyright (c) 2012 - 2016, GitHub, Inc.** 41 | **All rights reserved.** 42 | 43 | Permission is hereby granted, free of charge, to any person obtaining a copy of 44 | this software and associated documentation files (the "Software"), to deal in 45 | the Software without restriction, including without limitation the rights to 46 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 47 | the Software, and to permit persons to whom the Software is furnished to do so, 48 | subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all 51 | copies or substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 54 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 55 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 56 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 57 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 58 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KsApi 2 | 3 | > **Moved!** This library has been incorporated into [the main Kickstarter repo](https://github.com/kickstarter/ios-oss). 4 | 5 | A library for interacting with Kickstarter's API. 6 | -------------------------------------------------------------------------------- /bin/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p Frameworks/native-secrets/ios 4 | cp -n Configs/Secrets.swift.example Frameworks/native-secrets/ios/Secrets.swift 5 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o pipefail 4 | 5 | if [ $# -eq 0 ]; then 6 | echo "Please specify a target, i.e. iOS or tvOS." 7 | exit 1 8 | fi 9 | 10 | if [ "$1" == "iOS" ]; then 11 | DESTINATION='platform=iOS Simulator,name=iPhone 6,OS=' 12 | else 13 | DESTINATION='platform=tvOS Simulator,name=Apple TV 1080p,OS=' 14 | fi 15 | 16 | xcodebuild \ 17 | -destination "$DESTINATION$2" \ 18 | -scheme KsApi-$1 \ 19 | clean test \ 20 | | tee $CIRCLE_ARTIFACTS/xcode_raw.log \ 21 | | xcpretty 22 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | xcode: 3 | version: 8.3 4 | dependencies: 5 | pre: 6 | - bin/bootstrap 7 | - brew update || brew update 8 | - brew install swiftlint 9 | - system_profiler SPSoftwareDataType 10 | - security list-keychains 11 | - security find-identity -p codesigning 12 | - instruments -s devices 13 | - xcodebuild -showsdks 14 | override: 15 | - git submodule sync --recursive 16 | - git submodule update --init --recursive || git submodule foreach git fetch origin --tags 17 | - git submodule update --init --recursive 18 | test: 19 | pre: 20 | - xcrun instruments -w 'iPhone 6 (9.3)' || true 21 | - sleep 15 22 | override: 23 | - set -o pipefail && 24 | swiftlint lint --strict --reporter json | 25 | tee $CIRCLE_ARTIFACTS/swiftlint-report.json 26 | - bin/test iOS 9.3 27 | - bin/test iOS 10.2 28 | - bin/test tvOS 10.0 29 | experimental: 30 | notify: 31 | branches: 32 | only: 33 | - master 34 | --------------------------------------------------------------------------------