├── .ruby-version ├── PutioAPI ├── Assets │ └── .gitkeep └── Classes │ ├── Files │ ├── FileSearchModel.swift │ ├── MediaFileModel.swift │ ├── FileSearchAPI.swift │ ├── MediaFileAPI.swift │ ├── FilesModel.swift │ └── FilesAPI.swift │ ├── Extensions │ └── Dictionary+Merge.swift │ ├── Routes │ ├── RoutesModel.swift │ └── RoutesAPI.swift │ ├── HistoryEvents │ ├── EventTypes │ │ ├── ZipCreatedEvent.swift │ │ ├── TransferErrorEvent.swift │ │ ├── RSSFilterPausedEvent.swift │ │ ├── UploadEvent.swift │ │ ├── VoucherEvent.swift │ │ ├── FileFromRSSDeletedErrorEvent.swift │ │ ├── TransferCallbackErrorEvent.swift │ │ ├── PrivateTorrentPinEvent.swift │ │ ├── TransferFromRSSErrorEvent.swift │ │ ├── FileSharedEvent.swift │ │ └── TransferCompletedEvent.swift │ ├── HistoryEventsModel.swift │ ├── HistoryEventFactory.swift │ └── HistoryEventsAPI.swift │ ├── Grants │ ├── GrantsModel.swift │ └── GrantsAPI.swift │ ├── IFTTT │ ├── IFTTTAPI.swift │ └── IFTTTModel.swift │ ├── Subtitles │ ├── SubtitlesAPI.swift │ └── SubtitlesModel.swift │ ├── Trash │ ├── TrashModel.swift │ └── TrashAPI.swift │ ├── Auth │ ├── AuthModel.swift │ └── AuthAPI.swift │ ├── Account │ ├── AccountAPI.swift │ └── AccountModel.swift │ ├── PutioAPITypes.swift │ └── PutioAPI.swift ├── _Pods.xcodeproj ├── Gemfile ├── Example ├── PutioAPI.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── PutioAPI-Example.xcscheme │ └── project.pbxproj ├── PutioAPI.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Podfile ├── Podfile.lock ├── PutioAPI │ ├── AppDelegate.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ └── ViewController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── .gitignore ├── PutioAPI.podspec ├── LICENSE ├── README.md └── Gemfile.lock /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.8 2 | -------------------------------------------------------------------------------- /PutioAPI/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gem 'rexml' 5 | gem 'cocoapods' 6 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/FileSearchModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioFileSearchResponse { 5 | open var cursor: String 6 | open var files: [PutioFile] 7 | 8 | init(json: JSON) { 9 | self.cursor = json["cursor"].stringValue 10 | self.files = json["files"].arrayValue.map {PutioFile(json: $0)} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Extensions/Dictionary+Merge.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Dictionary { 4 | func merge(with source: [Key: Value]) -> Dictionary { 5 | var result: [Key: Value] = [:] 6 | 7 | for (key, value) in self { 8 | result[key] = value 9 | } 10 | 11 | for (key, value) in source { 12 | result[key] = value 13 | } 14 | 15 | return result 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Routes/RoutesModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioRoute { 5 | open var name: String 6 | open var description: String 7 | open var hosts: [String] 8 | 9 | init(json: JSON) { 10 | self.name = json["name"].stringValue 11 | self.description = json["description"].stringValue 12 | self.hosts = json["hosts"].arrayValue.map {$0.stringValue} 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/ZipCreatedEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioZipCreatedEvent: PutioHistoryEvent { 4 | open var zipID: Int 5 | open var zipSize: Int64 6 | 7 | override init(json: JSON) { 8 | self.zipID = json["zip_id"].intValue 9 | self.zipSize = json["zip_size"].int64Value 10 | 11 | super.init(json: json) 12 | 13 | self.type = .zipCreated 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '13.0' 2 | 3 | use_frameworks! 4 | 5 | target 'PutioAPI_Example' do 6 | pod 'PutioAPI', :path => '../' 7 | 8 | target 'PutioAPI_Tests' do 9 | inherit! :search_paths 10 | end 11 | end 12 | 13 | post_install do |installer| 14 | installer.pods_project.targets.each do |target| 15 | target.build_configurations.each do |config| 16 | config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Grants/GrantsModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioOAuthGrant { 5 | open var id: Int 6 | open var name: String 7 | open var description: String 8 | open var website: URL? 9 | 10 | init(json: JSON) { 11 | self.id = json["id"].intValue 12 | self.name = json["name"].stringValue 13 | self.description = json["description"].stringValue 14 | self.website = json["website"].url 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/TransferErrorEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioTransferErrorEvent: PutioHistoryEvent { 4 | open var source: String 5 | open var transferName: String 6 | 7 | override init(json: JSON) { 8 | self.source = json["source"].stringValue 9 | self.transferName = json["transfer_name"].stringValue 10 | 11 | super.init(json: json) 12 | 13 | self.type = .transferError 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/RSSFilterPausedEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioRSSFilterPausedEvent: PutioHistoryEvent { 4 | open var rssFilterID: Int 5 | open var rssFilterTitle: String 6 | 7 | override init(json: JSON) { 8 | self.rssFilterID = json["rss_filter_id"].intValue 9 | self.rssFilterTitle = json["rss_filter_title"].stringValue 10 | 11 | super.init(json: json) 12 | 13 | self.type = .rssFilterPaused 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/UploadEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioUploadEvent: PutioHistoryEvent, PutioFileHistoryEvent { 4 | open var fileName: String 5 | open var fileSize: Int64 6 | open var fileID: Int 7 | 8 | override init(json: JSON) { 9 | self.fileName = json["file_name"].stringValue 10 | self.fileSize = json["file_size"].int64Value 11 | self.fileID = json["file_id"].intValue 12 | 13 | super.init(json: json) 14 | 15 | self.type = .upload 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Routes/RoutesAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | extension PutioAPI { 5 | public func getRoutes(completion: @escaping (Result<[PutioRoute], PutioAPIError>) -> Void) { 6 | self.get("/tunnel/routes") { result in 7 | switch result { 8 | case .success(let json): 9 | return completion(.success(json["routes"].arrayValue.map { PutioRoute(json: $0) })) 10 | case .failure(let error): 11 | return completion(.failure(error)) 12 | 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/VoucherEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioVoucherEvent: PutioHistoryEvent { 4 | open var voucherID: Int 5 | open var voucherOwnerID: Int 6 | open var voucherOwnerName: String 7 | 8 | override init(json: JSON) { 9 | self.voucherID = json["voucher"].intValue 10 | self.voucherOwnerID = json["voucher_owner_id"].intValue 11 | self.voucherOwnerName = json["voucher_owner_name"].stringValue 12 | 13 | super.init(json: json) 14 | 15 | self.type = .voucher 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/IFTTT/IFTTTAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension PutioAPI { 4 | public func sendIFTTTEvent(event: PutioIFTTTEvent, completion: @escaping PutioAPIBoolCompletion) { 5 | self.post("/ifttt-client/event", body: ["event_type": event.eventType, "ingredients": event.ingredients.toJSON()]) { result in 6 | switch result { 7 | case .success(let json): 8 | return completion(.success(json)) 9 | case .failure(let error): 10 | return completion(.failure(error)) 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/FileFromRSSDeletedErrorEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioFileFromRSSDeletedErrorEvent: PutioHistoryEvent { 4 | open var fileName: String 5 | open var fileSource: String 6 | open var fileSize: Int64 7 | 8 | override init(json: JSON) { 9 | self.fileName = json["file_name"].stringValue 10 | self.fileSource = json["file_source"].stringValue 11 | self.fileSize = json["file_size"].int64Value 12 | 13 | super.init(json: json) 14 | 15 | self.type = .fileFromRSSDeletedError 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/TransferCallbackErrorEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioTransferCallbackErrorEvent: PutioHistoryEvent { 4 | open var transferID: Int 5 | open var transferName: String 6 | open var message: String 7 | 8 | override init(json: JSON) { 9 | self.transferID = json["transfer_id"].intValue 10 | self.transferName = json["transfer_name"].stringValue 11 | self.message = json["message"].stringValue 12 | 13 | super.init(json: json) 14 | 15 | self.type = .transferCallbackError 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/PrivateTorrentPinEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioPrivateTorrentPinEvent: PutioHistoryEvent { 4 | open var userDownloadName: String 5 | open var pinnedHostIP: String 6 | open var newHostIP: String 7 | 8 | override init(json: JSON) { 9 | self.userDownloadName = json["user_download_name"].stringValue 10 | self.pinnedHostIP = json["pinned_host_ip"].stringValue 11 | self.newHostIP = json["new_host_ip"].stringValue 12 | 13 | super.init(json: json) 14 | 15 | self.type = .privateTorrentPin 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Subtitles/SubtitlesAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension PutioAPI { 4 | public func getSubtitles(fileID: Int, completion: @escaping (Result<[PutioSubtitle], PutioAPIError>) -> Void) { 5 | self.get("/files/\(fileID)/subtitles", query: ["oauth_token": self.config.token]) { result in 6 | switch result { 7 | case .success(let json): 8 | return completion(.success(json["subtitles"].arrayValue.map {PutioSubtitle(json: $0)})) 9 | case .failure(let error): 10 | return completion(.failure(error)) 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/TransferFromRSSErrorEvent.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransferFromRSSErrorEvent.swift 3 | // PutioAPI 4 | // 5 | // Created by Batuhan Aksoy on 14.09.2020. 6 | // 7 | 8 | import SwiftyJSON 9 | 10 | open class PutioTransferFromRSSErrorEvent: PutioHistoryEvent { 11 | open var rssID: Int 12 | open var transferName: String 13 | 14 | override init(json: JSON) { 15 | self.rssID = json["rss_id"].intValue 16 | self.transferName = json["transfer_name"].stringValue 17 | 18 | super.init(json: json) 19 | 20 | self.type = .transferFromRSSError 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (5.5.0) 3 | - PutioAPI (1.4.0): 4 | - Alamofire (~> 5.5.0) 5 | - SwiftyJSON (~> 5.0) 6 | - SwiftyJSON (5.0.0) 7 | 8 | DEPENDENCIES: 9 | - PutioAPI (from `../`) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Alamofire 14 | - SwiftyJSON 15 | 16 | EXTERNAL SOURCES: 17 | PutioAPI: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Alamofire: 1c4fb5369c3fe93d2857c780d8bbe09f06f97e7c 22 | PutioAPI: b5a1c21bbe7ac000eb09a6b2579066be593e3091 23 | SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 24 | 25 | PODFILE CHECKSUM: 30c7d4bdcfd2b66bd0be195157e82e345fbb9f19 26 | 27 | COCOAPODS: 1.11.2 28 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/MediaFileModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioMp4Conversion { 5 | public enum Status: String { 6 | case queued = "IN_QUEUE", 7 | converting = "CONVERTING", 8 | completed = "COMPLETED", 9 | error = "ERROR", 10 | notAvailable = "NOT_AVAILABLE" 11 | } 12 | 13 | open var percentDone: Float 14 | open var status: Status 15 | 16 | init(json: JSON) { 17 | let mp4 = json["mp4"] 18 | self.percentDone = mp4["percent_done"].floatValue / 100 19 | self.status = Status.init(rawValue: mp4["status"].stringValue)! 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Subtitles/SubtitlesModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioSubtitle { 5 | open var key: String 6 | open var language: String 7 | open var languageCode: String 8 | open var name: String 9 | open var source: String 10 | open var url: String 11 | 12 | init(json: JSON) { 13 | self.key = json["key"].stringValue 14 | self.language = json["language"].stringValue 15 | self.languageCode = json["language_code"].stringValue 16 | self.name = json["name"].stringValue 17 | self.source = json["source"].stringValue 18 | self.url = json["url"].stringValue 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/FileSharedEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioFileSharedEvent: PutioHistoryEvent, PutioFileHistoryEvent { 4 | open var sharingUserName: String 5 | open var fileName: String 6 | open var fileSize: Int64 7 | open var fileID: Int 8 | 9 | override init(json: JSON) { 10 | self.sharingUserName = json["sharing_user_name"].stringValue 11 | self.fileName = json["file_name"].stringValue 12 | self.fileSize = json["file_size"].int64Value 13 | self.fileID = json["file_id"].intValue 14 | 15 | super.init(json: json) 16 | 17 | self.type = .fileShared 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/EventTypes/TransferCompletedEvent.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioTransferCompletedEvent: PutioHistoryEvent, PutioFileHistoryEvent { 4 | open var transferName: String 5 | open var transferSize: Int64 6 | open var source: String 7 | open var fileID: Int 8 | 9 | override init(json: JSON) { 10 | self.transferName = json["transfer_name"].stringValue 11 | self.transferSize = json["transfer_size"].int64Value 12 | self.source = json["source"].stringValue 13 | self.fileID = json["file_id"].intValue 14 | 15 | super.init(json: json) 16 | 17 | self.type = .transferCompleted 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Example/PutioAPI/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | var window: UIWindow? 6 | 7 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 8 | return true 9 | } 10 | 11 | func applicationWillResignActive(_ application: UIApplication) {} 12 | 13 | func applicationDidEnterBackground(_ application: UIApplication) {} 14 | 15 | func applicationWillEnterForeground(_ application: UIApplication) {} 16 | 17 | func applicationDidBecomeActive(_ application: UIApplication) {} 18 | 19 | func applicationWillTerminate(_ application: UIApplication) {} 20 | } 21 | -------------------------------------------------------------------------------- /Example/Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import PutioAPI 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | Example/Pods 38 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Trash/TrashModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioTrashFile: PutioBaseFile { 5 | open var deletedAt: Date 6 | open var expiresOn: Date 7 | 8 | override init(json: JSON) { 9 | let formatter = ISO8601DateFormatter() 10 | self.deletedAt = formatter.date(from: json["deleted_at"].stringValue) ?? formatter.date(from: "\(json["deleted_at"].stringValue)+00:00")! 11 | self.expiresOn = formatter.date(from: json["expiration_date"].stringValue) ?? formatter.date(from: "\(json["expiration_date"].stringValue)+00:00")! 12 | super.init(json: json) 13 | } 14 | } 15 | 16 | 17 | open class PutioListTrashResponse { 18 | open var cursor: String 19 | open var trash_size: Int64 20 | open var files: [PutioTrashFile] 21 | 22 | init(json: JSON) { 23 | self.cursor = json["cursor"].stringValue 24 | self.trash_size = json["trash_size"].int64Value 25 | self.files = json["files"].arrayValue.map { PutioTrashFile(json: $0) } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/HistoryEventsModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | 5 | public protocol PutioFileHistoryEvent { 6 | var fileID: Int { get set } 7 | } 8 | 9 | open class PutioHistoryEvent { 10 | public enum EventType { 11 | case upload, fileShared, transferCompleted, transferError, fileFromRSSDeletedError, rssFilterPaused, transferFromRSSError, transferCallbackError, privateTorrentPin, voucher, zipCreated, other 12 | } 13 | 14 | open var id: Int 15 | open var userID: Int 16 | open var type: EventType 17 | open var createdAt: Date 18 | 19 | init(json: JSON) { 20 | self.id = json["id"].intValue 21 | self.userID = json["user_id"].intValue 22 | 23 | self.type = .other 24 | 25 | // Put.io API currently does not provide dates compatible with iso8601, but can do in the future. 26 | let formatter = ISO8601DateFormatter() 27 | self.createdAt = formatter.date(from: json["created_at"].stringValue) ?? formatter.date(from: "\(json["created_at"].stringValue)+00:00")! 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PutioAPI.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint PutioAPI.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'PutioAPI' 11 | s.version = '1.5.0' 12 | s.swift_version = '4.2' 13 | 14 | s.summary = 'Swift client for put.io API.' 15 | s.description = 'Swift client for [put.io API](https://api.put.io).' 16 | 17 | s.license = { :type => 'MIT', :file => 'LICENSE' } 18 | s.author = { 'put.io' => 'ui@put.io' } 19 | 20 | s.homepage = 'https://github.com/putdotio/putio-swift' 21 | s.source = { :git => 'https://github.com/putdotio/putio-swift.git', :tag => s.version.to_s } 22 | s.social_media_url = 'https://twitter.com/putdotio' 23 | 24 | s.ios.deployment_target = '13.0' 25 | s.source_files = 'PutioAPI/Classes/**/*' 26 | 27 | s.dependency 'Alamofire', '~> 5.5.0' 28 | s.dependency 'SwiftyJSON', '~> 5.0' 29 | end 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 put.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Example/PutioAPI/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/FileSearchAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension PutioAPI { 4 | public func searchFiles(keyword: String, perPage: Int = 50, completion: @escaping (Result) -> Void) { 5 | let url = "/files/search" 6 | let query = ["query": keyword, "per_page": perPage] as [String : Any] 7 | 8 | self.get(url, query: query) { result in 9 | switch result { 10 | case .success(let json): 11 | return completion(.success(PutioFileSearchResponse(json: json))) 12 | case .failure(let error): 13 | return completion(.failure(error)) 14 | } 15 | } 16 | } 17 | 18 | public func continueFileSearch(cursor: String, completion: @escaping (Result) -> Void) { 19 | let url = "/files/search/continue" 20 | let query = ["cursor": cursor] 21 | 22 | self.get(url, query: query) { result in 23 | switch result { 24 | case .success(let json): 25 | return completion(.success(PutioFileSearchResponse(json: json))) 26 | case .failure(let error): 27 | return completion(.failure(error)) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /PutioAPI/Classes/IFTTT/IFTTTModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public class PutioIFTTTEvent { 4 | open var eventType: String 5 | open var ingredients: PutioIFTTTEventIngredients 6 | 7 | public init(eventType: String, ingredients: PutioIFTTTEventIngredients) { 8 | self.eventType = eventType 9 | self.ingredients = ingredients 10 | } 11 | } 12 | 13 | public class PutioIFTTTEventIngredients { 14 | public func toJSON() -> [String: Any] { 15 | return [:] 16 | } 17 | } 18 | 19 | public class PutioIFTTTPlaybackEventIngredients: PutioIFTTTEventIngredients { 20 | public var fileId: Int 21 | public var fileName: String 22 | public var fileType: String 23 | 24 | public init(fileId: Int, fileName: String, fileType: String) { 25 | self.fileId = fileId 26 | self.fileName = fileName 27 | self.fileType = fileType 28 | } 29 | 30 | override public func toJSON() -> [String: Any] { 31 | return [ 32 | "file_id": self.fileId, 33 | "file_name": self.fileName, 34 | "file_type": self.fileType 35 | ] 36 | } 37 | } 38 | 39 | public class PutioIFTTTPlaybackEvent: PutioIFTTTEvent { 40 | public init(eventType: String, ingredients: PutioIFTTTPlaybackEventIngredients) { 41 | super.init(eventType: eventType, ingredients: ingredients) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/HistoryEventFactory.swift: -------------------------------------------------------------------------------- 1 | import SwiftyJSON 2 | 3 | open class PutioHistoryEventFactory { 4 | static func get(json: JSON) -> PutioHistoryEvent { 5 | switch json["type"] { 6 | case "upload": 7 | return PutioUploadEvent(json: json) 8 | case "file_shared": 9 | return PutioFileSharedEvent(json: json) 10 | case "transfer_completed": 11 | return PutioTransferCompletedEvent(json: json) 12 | case "transfer_error": 13 | return PutioTransferErrorEvent(json: json) 14 | case "file_from_rss_deleted_for_space": 15 | return PutioFileFromRSSDeletedErrorEvent(json: json) 16 | case "rss_filter_paused": 17 | return PutioRSSFilterPausedEvent(json: json) 18 | case "transfer_from_rss_error": 19 | return PutioTransferFromRSSErrorEvent(json: json) 20 | case "transfer_callback_error": 21 | return PutioTransferCallbackErrorEvent(json: json) 22 | case "private_torrent_pin": 23 | return PutioPrivateTorrentPinEvent(json: json) 24 | case "voucher": 25 | return PutioVoucherEvent(json: json) 26 | case "zip_created": 27 | return PutioZipCreatedEvent(json: json) 28 | default: 29 | return PutioHistoryEvent(json: json) 30 | } 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /PutioAPI/Classes/HistoryEvents/HistoryEventsAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | extension PutioAPI { 5 | public func getHistoryEvents(completion: @escaping (Result<[PutioHistoryEvent], PutioAPIError>) -> Void) { 6 | self.get("/events/list") { result in 7 | switch result { 8 | case .success(let json): 9 | return completion(.success(json["events"].arrayValue.map {PutioHistoryEventFactory.get(json: $0)})) 10 | case .failure(let error): 11 | return completion(.failure(error)) 12 | } 13 | } 14 | } 15 | 16 | public func clearHistoryEvents(completion: @escaping PutioAPIBoolCompletion) { 17 | self.post("/events/delete") { result in 18 | switch result { 19 | case .success(let json): 20 | return completion(.success(json)) 21 | case .failure(let error): 22 | return completion(.failure(error)) 23 | } 24 | } 25 | } 26 | 27 | public func deleteHistoryEvent(eventID: Int, completion: @escaping PutioAPIBoolCompletion) { 28 | self.post("/events/delete/\(eventID)") { result in 29 | switch result { 30 | case .success(let json): 31 | return completion(.success(json)) 32 | case .failure(let error): 33 | return completion(.failure(error)) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Grants/GrantsAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | extension PutioAPI { 5 | public func getGrants(completion: @escaping (Result<[PutioOAuthGrant], PutioAPIError>) -> Void) { 6 | self.get("/oauth/grants") { result in 7 | switch result { 8 | case .success(let json): 9 | return completion(.success(json["apps"].arrayValue.map {PutioOAuthGrant(json: $0)})) 10 | case .failure(let error): 11 | return completion(.failure(error)) 12 | } 13 | } 14 | } 15 | 16 | public func revokeGrant(id: Int, completion: @escaping PutioAPIBoolCompletion) { 17 | self.post("/oauth/grants/\(id)/delete") { result in 18 | switch result { 19 | case .success(let json): 20 | return completion(.success(json)) 21 | case .failure(let error): 22 | return completion(.failure(error)) 23 | } 24 | } 25 | } 26 | 27 | public func linkDevice(code: String, completion: @escaping (Result) -> Void) { 28 | self.post("/oauth2/oob/code", body: ["code": code]) { result in 29 | switch result { 30 | case .success(let json): 31 | return completion(.success(PutioOAuthGrant(json: json["app"]))) 32 | case .failure(let error): 33 | return completion(.failure(error)) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/PutioAPI/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Viewer 26 | CFBundleURLName 27 | org.cocoapods.demo.PutioAPI-Example 28 | CFBundleURLSchemes 29 | 30 | putioswift 31 | 32 | 33 | 34 | 35 | CFBundleVersion 36 | 1 37 | LSRequiresIPhoneOS 38 | 39 | UILaunchStoryboardName 40 | LaunchScreen 41 | UIMainStoryboardFile 42 | Main 43 | UIRequiredDeviceCapabilities 44 | 45 | armv7 46 | 47 | UISupportedInterfaceOrientations 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationLandscapeLeft 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 |

5 | 6 |

putio-swift

7 | 8 |

9 | Swift SDK for interacting with the put.io API. 10 |

11 | 12 |

13 | Cocoapods 14 | GitHub 15 |

16 |
17 | 18 | ## Installation 19 | 20 | `PutioAPI` is available through [CocoaPods](https://cocoapods.org/pods/PutioAPI). To install, simply add the following line to your Podfile: 21 | 22 | ```ruby 23 | pod 'PutioAPI' 24 | ``` 25 | 26 | ## Usage 27 | 28 | - For authentication, check the [Example Project](./Example/PutioAPI/ViewController.swift) for a simple [`ASWebAuthenticationSession`](https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service) flow. 29 | - Check [the classes folder](./PutioAPI/Classes/) for available models and respective methods. 30 | - You can also use `get`, `post`, `put`, and `delete` methods with relative URLs to make requests to the API. 31 | 32 | ## Contribution 33 | 34 | Clone the repo. 35 | 36 | ```bash 37 | git clone git@github.com:putdotio/putio-swift.git 38 | cd ./putio-swift 39 | ``` 40 | 41 | Install the package managers, it's suggested to use `rbenv` and `bundler` for convenience. 42 | 43 | ```bash 44 | gem install bundler # if you don't have bundler 45 | bundle install 46 | ``` 47 | 48 | Install the dependencies then open the workspace. 49 | 50 | ```bash 51 | cd ./Example 52 | bundle exec pod install 53 | open ./PutioAPI.xcworkspace 54 | ``` 55 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Auth/AuthModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioTokenValidationResult { 5 | open var result: Bool 6 | open var token_id: Int 7 | open var token_scope: String 8 | open var user_id: Int 9 | 10 | init(json: JSON) { 11 | self.result = json["result"].boolValue 12 | self.token_id = json["token_id"].intValue 13 | self.token_scope = json["token_scope"].stringValue 14 | self.user_id = json["user_id"].intValue 15 | } 16 | } 17 | 18 | open class PutioTwoFactorRecoveryCode { 19 | open var code: String 20 | open var used_at: String? 21 | 22 | init(json: JSON) { 23 | self.code = json["code"].stringValue 24 | self.used_at = json["used_at"].stringValue 25 | } 26 | } 27 | 28 | open class PutioTwoFactorRecoveryCodes { 29 | open var created_at: String 30 | open var codes: [PutioTwoFactorRecoveryCode] 31 | 32 | init(json: JSON) { 33 | self.created_at = json["created_at"].stringValue 34 | self.codes = json["codes"].arrayValue.map { PutioTwoFactorRecoveryCode(json: $0) } 35 | } 36 | } 37 | 38 | open class PutioGenerateTOTPResult { 39 | open var secret: String 40 | open var uri: String 41 | open var recovery_codes: PutioTwoFactorRecoveryCodes 42 | 43 | init(json: JSON) { 44 | self.secret = json["secret"].stringValue 45 | self.uri = json["uri"].stringValue 46 | self.recovery_codes = PutioTwoFactorRecoveryCodes(json: json["recovery_codes"]) 47 | } 48 | } 49 | 50 | open class PutioVerifyTOTPResult { 51 | open var token: String 52 | open var user_id: Int 53 | 54 | init(json: JSON) { 55 | self.token = json["token"].stringValue 56 | self.user_id = json["user_id"].intValue 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Account/AccountAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Alamofire 3 | import SwiftyJSON 4 | 5 | extension PutioAPI { 6 | public func getAccountInfo(query: Parameters = [:], completion: @escaping (Result) -> Void) { 7 | self.get("/account/info", query: query) { result in 8 | switch result { 9 | case .success(let json): 10 | return completion(.success(PutioAccount(json: json["info"]))) 11 | case .failure(let error): 12 | return completion(.failure(error)) 13 | } 14 | } 15 | } 16 | 17 | public func getAccountSettings(completion: @escaping (Result) -> Void) { 18 | self.get("/account/settings") { result in 19 | switch result { 20 | case .success(let json): 21 | return completion(.success(PutioAccount.Settings(json: json["settings"]))) 22 | case .failure(let error): 23 | return completion(.failure(error)) 24 | } 25 | } 26 | } 27 | 28 | public func saveAccountSettings(body: [String: Any], completion: @escaping PutioAPIBoolCompletion) { 29 | self.post("/account/settings", body: body) { result in 30 | switch result { 31 | case .success(let json): 32 | return completion(.success(json)) 33 | case .failure(let error): 34 | return completion(.failure(error)) 35 | } 36 | } 37 | } 38 | 39 | public func clearAccountData(options: [String: Bool], completion: @escaping PutioAPIBoolCompletion) { 40 | self.post("/account/clear", body: options) { result in 41 | switch result { 42 | case .success(let json): 43 | return completion(.success(json)) 44 | case .failure(let error): 45 | return completion(.failure(error)) 46 | } 47 | } 48 | } 49 | 50 | public func destroyAccount(currentPassword: String, completion: @escaping PutioAPIBoolCompletion) { 51 | self.post("/account/destroy", body: ["current_password": currentPassword]) { result in 52 | switch result { 53 | case .success(let json): 54 | return completion(.success(json)) 55 | case .failure(let error): 56 | return completion(.failure(error)) 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/MediaFileAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | extension PutioAPI { 5 | public func startMp4Conversion(fileID: Int, completion: @escaping PutioAPIBoolCompletion) { 6 | let url = "/files/\(fileID)/mp4" 7 | 8 | self.post(url) { result in 9 | switch result { 10 | case .success(let json): 11 | return completion(.success(json)) 12 | case .failure(let error): 13 | return completion(.failure(error)) 14 | } 15 | } 16 | } 17 | 18 | public func getMp4ConversionStatus(fileID: Int, completion: @escaping (Result) -> Void) { 19 | let url = "/files/\(fileID)/mp4" 20 | 21 | self.get(url) { result in 22 | switch result { 23 | case .success(let json): 24 | return completion(.success(PutioMp4Conversion(json: json))) 25 | case .failure(let error): 26 | return completion(.failure(error)) 27 | } 28 | } 29 | } 30 | 31 | public func getStartFrom(fileID: Int, completion: @escaping (Result) -> Void) { 32 | let url = "/files/\(fileID)/start-from" 33 | 34 | self.get(url) { result in 35 | switch result { 36 | case .success(let json): 37 | return completion(.success(json["start_from"].intValue)) 38 | case .failure(let error): 39 | return completion(.failure(error)) 40 | } 41 | } 42 | } 43 | 44 | public func setStartFrom(fileID: Int, time: Int, completion: @escaping PutioAPIBoolCompletion) { 45 | let url = "/files/\(fileID)/start-from/set" 46 | 47 | self.post(url, body: ["time": time]) { result in 48 | switch result { 49 | case .success(let json): 50 | return completion(.success(json)) 51 | case .failure(let error): 52 | return completion(.failure(error)) 53 | } 54 | } 55 | } 56 | 57 | public func resetStartFrom(fileID: Int, completion: @escaping PutioAPIBoolCompletion) { 58 | let url = "/files/\(fileID)/start-from/delete" 59 | 60 | self.get(url) { result in 61 | switch result { 62 | case .success(let json): 63 | return completion(.success(json)) 64 | case .failure(let error): 65 | return completion(.failure(error)) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.5) 5 | rexml 6 | activesupport (6.1.4.4) 7 | concurrent-ruby (~> 1.0, >= 1.0.2) 8 | i18n (>= 1.6, < 2) 9 | minitest (>= 5.1) 10 | tzinfo (~> 2.0) 11 | zeitwerk (~> 2.3) 12 | addressable (2.8.0) 13 | public_suffix (>= 2.0.2, < 5.0) 14 | algoliasearch (1.27.5) 15 | httpclient (~> 2.8, >= 2.8.3) 16 | json (>= 1.5.1) 17 | atomos (0.1.3) 18 | claide (1.0.3) 19 | cocoapods (1.11.2) 20 | addressable (~> 2.8) 21 | claide (>= 1.0.2, < 2.0) 22 | cocoapods-core (= 1.11.2) 23 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 24 | cocoapods-downloader (>= 1.4.0, < 2.0) 25 | cocoapods-plugins (>= 1.0.0, < 2.0) 26 | cocoapods-search (>= 1.0.0, < 2.0) 27 | cocoapods-trunk (>= 1.4.0, < 2.0) 28 | cocoapods-try (>= 1.1.0, < 2.0) 29 | colored2 (~> 3.1) 30 | escape (~> 0.0.4) 31 | fourflusher (>= 2.3.0, < 3.0) 32 | gh_inspector (~> 1.0) 33 | molinillo (~> 0.8.0) 34 | nap (~> 1.0) 35 | ruby-macho (>= 1.0, < 3.0) 36 | xcodeproj (>= 1.21.0, < 2.0) 37 | cocoapods-core (1.11.2) 38 | activesupport (>= 5.0, < 7) 39 | addressable (~> 2.8) 40 | algoliasearch (~> 1.0) 41 | concurrent-ruby (~> 1.1) 42 | fuzzy_match (~> 2.0.4) 43 | nap (~> 1.0) 44 | netrc (~> 0.11) 45 | public_suffix (~> 4.0) 46 | typhoeus (~> 1.0) 47 | cocoapods-deintegrate (1.0.5) 48 | cocoapods-downloader (1.5.1) 49 | cocoapods-plugins (1.0.0) 50 | nap 51 | cocoapods-search (1.0.1) 52 | cocoapods-trunk (1.6.0) 53 | nap (>= 0.8, < 2.0) 54 | netrc (~> 0.11) 55 | cocoapods-try (1.2.0) 56 | colored2 (3.1.2) 57 | concurrent-ruby (1.1.9) 58 | escape (0.0.4) 59 | ethon (0.15.0) 60 | ffi (>= 1.15.0) 61 | ffi (1.15.4) 62 | fourflusher (2.3.1) 63 | fuzzy_match (2.0.4) 64 | gh_inspector (1.1.3) 65 | httpclient (2.8.3) 66 | i18n (1.8.11) 67 | concurrent-ruby (~> 1.0) 68 | json (2.6.1) 69 | minitest (5.15.0) 70 | molinillo (0.8.0) 71 | nanaimo (0.3.0) 72 | nap (1.1.0) 73 | netrc (0.11.0) 74 | public_suffix (4.0.6) 75 | rexml (3.2.5) 76 | ruby-macho (2.5.1) 77 | typhoeus (1.4.0) 78 | ethon (>= 0.9.0) 79 | tzinfo (2.0.4) 80 | concurrent-ruby (~> 1.0) 81 | xcodeproj (1.21.0) 82 | CFPropertyList (>= 2.3.3, < 4.0) 83 | atomos (~> 0.1.3) 84 | claide (>= 1.0.2, < 2.0) 85 | colored2 (~> 3.1) 86 | nanaimo (~> 0.3.0) 87 | rexml (~> 3.2.4) 88 | zeitwerk (2.5.3) 89 | 90 | PLATFORMS 91 | arm64-darwin-23 92 | x86_64-darwin-20 93 | x86_64-linux 94 | 95 | DEPENDENCIES 96 | cocoapods 97 | rexml 98 | 99 | BUNDLED WITH 100 | 2.4.21 101 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Trash/TrashAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | extension PutioAPI { 5 | public func listTrash(perPage: Int = 50, completion: @escaping (Result) -> Void) { 6 | self.get("/trash/list", query: ["per_page": perPage]) { result in 7 | switch result { 8 | case .success(let json): 9 | return completion(.success(PutioListTrashResponse(json: json))) 10 | case .failure(let error): 11 | return completion(.failure(error)) 12 | } 13 | } 14 | } 15 | 16 | public func continueListTrash(cursor: String, perPage: Int = 50, completion: @escaping (Result) -> Void) { 17 | self.get("/trash/list/continue", query: ["cursor": cursor, "per_page": perPage]) { result in 18 | switch result { 19 | case .success(let json): 20 | return completion(.success(PutioListTrashResponse(json: json))) 21 | case .failure(let error): 22 | return completion(.failure(error)) 23 | } 24 | } 25 | } 26 | 27 | public func restoreTrashFiles(fileIDs: [Int] = [], cursor: String?, completion: @escaping PutioAPIBoolCompletion) { 28 | var body: [String: Any] = [:] 29 | 30 | if let cursor = cursor, cursor != "" { 31 | body = ["cursor": cursor] 32 | } else { 33 | body = ["file_ids": (fileIDs.map {String($0)}).joined(separator: ",")] 34 | } 35 | 36 | self.post("/trash/restore", body: body) { result in 37 | switch result { 38 | case .success(let json): 39 | return completion(.success(json)) 40 | case .failure(let error): 41 | return completion(.failure(error)) 42 | } 43 | } 44 | } 45 | 46 | public func deleteTrashFiles(fileIDs: [Int] = [], cursor: String?, completion: @escaping PutioAPIBoolCompletion) { 47 | var body: [String: Any] = [:] 48 | 49 | if let cursor = cursor, cursor != "" { 50 | body = ["cursor": cursor] 51 | } else { 52 | body = ["file_ids": (fileIDs.map {String($0)}).joined(separator: ",")] 53 | } 54 | 55 | self.post("/trash/delete", body: body){ result in 56 | switch result { 57 | case .success(let json): 58 | return completion(.success(json)) 59 | case .failure(let error): 60 | return completion(.failure(error)) 61 | } 62 | } 63 | } 64 | 65 | public func emptyTrash(completion: @escaping PutioAPIBoolCompletion) { 66 | self.post("/trash/empty") { result in 67 | switch result { 68 | case .success(let json): 69 | return completion(.success(json)) 70 | case .failure(let error): 71 | return completion(.failure(error)) 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Account/AccountModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | open class PutioAccount { 5 | open var id: Int 6 | open var username: String 7 | open var mail: String 8 | open var avatarURL: String 9 | open var hash: String 10 | open var features: [String: Bool] 11 | open var downloadToken: String 12 | open var trashSize: Int64 13 | open var accountActive: Bool 14 | open var filesWillBeDeletedAt: String 15 | open var passwordLastChangedAt: String 16 | 17 | public class Disk { 18 | open var available: Int64 19 | open var size: Int64 20 | open var used: Int64 21 | 22 | init(json: JSON) { 23 | self.available = json["avail"].int64Value 24 | self.size = json["size"].int64Value 25 | self.used = json["used"].int64Value 26 | } 27 | } 28 | 29 | open var disk: Disk 30 | 31 | public class Settings { 32 | open var routeName: String 33 | open var suggestNextVideo: Bool 34 | open var rememberVideoTime: Bool 35 | open var historyEnabled: Bool 36 | open var trashEnabled: Bool 37 | open var sortBy: String 38 | open var showOptimisticUsage: Bool 39 | open var twoFactorEnabled: Bool 40 | open var hideSubtitles: Bool 41 | open var dontAutoSelectSubtitles: Bool 42 | 43 | init(json: JSON) { 44 | let routeName = json["tunnel_route_name"].stringValue 45 | self.sortBy = json["sort_by"].stringValue 46 | self.routeName = routeName == "" ? "default" : routeName 47 | self.suggestNextVideo = json["next_episode"].boolValue 48 | self.rememberVideoTime = json["start_from"].boolValue 49 | self.historyEnabled = json["history_enabled"].boolValue 50 | self.trashEnabled = json["trash_enabled"].boolValue 51 | self.showOptimisticUsage = json["show_optimistic_usage"].boolValue 52 | self.twoFactorEnabled = json["two_factor_enabled"].boolValue 53 | self.hideSubtitles = json["hide_subtitles"].boolValue 54 | self.dontAutoSelectSubtitles = json["dont_autoselect_subtitles"].boolValue 55 | } 56 | } 57 | 58 | open var settings: Settings 59 | 60 | init(json: JSON) { 61 | self.id = json["user_id"].intValue 62 | self.username = json["username"].stringValue 63 | self.mail = json["mail"].stringValue 64 | self.avatarURL = json["avatar_url"].stringValue 65 | self.hash = json["user_hash"].stringValue 66 | self.features = json["features"].dictionaryObject as? [String: Bool] ?? [:] 67 | self.downloadToken = json["download_token"].stringValue 68 | self.trashSize = json["trash_size"].int64Value 69 | self.accountActive = json["account_active"].boolValue 70 | self.filesWillBeDeletedAt = json["files_will_be_deleted_at"].stringValue 71 | self.passwordLastChangedAt = json["password_last_changed_at"].stringValue 72 | self.disk = Disk(json: json["disk"]) 73 | self.settings = Settings(json: json["settings"]) 74 | } 75 | } 76 | 77 | public var PutioClearDataOptionKeys = [ 78 | "files", 79 | "finished_transfers", 80 | "active_transfers", 81 | "rss_feeds", 82 | "rss_logs", 83 | "history", 84 | "trash", 85 | "friends" 86 | ] 87 | -------------------------------------------------------------------------------- /PutioAPI/Classes/PutioAPITypes.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Alamofire 3 | import SwiftyJSON 4 | 5 | public struct PutioAPIConfig { 6 | public let baseURL: String 7 | public var token: String 8 | public var clientID: String 9 | public var clientSecret: String 10 | public var clientName: String 11 | public var timeoutInterval: Double 12 | 13 | public init(clientID: String, clientSecret: String = "", clientName: String = "", token: String = "", timeoutInterval: Double = 15.0) { 14 | self.baseURL = PutioAPI.apiURL 15 | self.clientID = clientID 16 | self.clientSecret = clientSecret 17 | self.clientName = clientName 18 | self.token = token 19 | self.timeoutInterval = timeoutInterval 20 | } 21 | } 22 | 23 | public struct PutioAPIRequestConfig { 24 | let url: String 25 | let method: HTTPMethod 26 | let headers: HTTPHeaders 27 | let query: Parameters 28 | let body: Parameters? 29 | 30 | init(apiConfig: PutioAPIConfig, url: String, method: HTTPMethod, headers: HTTPHeaders = [:], query: Parameters = [:], body: Parameters = [:]) { 31 | if (query.isEmpty) { 32 | self.url = "\(apiConfig.baseURL)\(url)" 33 | } else { 34 | let encodedURLRequest = try! URLEncoding.queryString.encode(URLRequest(url: URL(string: url)!), with: query) 35 | self.url = "\(apiConfig.baseURL)\((encodedURLRequest.url?.absoluteString)!)" 36 | } 37 | 38 | self.method = method 39 | 40 | var enhancedHeaders = headers 41 | if enhancedHeaders.value(for: "authorization") == nil { 42 | if apiConfig.token != "" { 43 | let authorizationHeader = HTTPHeader.authorization("token \(apiConfig.token)") 44 | enhancedHeaders.add(authorizationHeader) 45 | } 46 | } 47 | 48 | self.headers = enhancedHeaders 49 | 50 | self.query = query 51 | 52 | switch method { 53 | case .post, .put, .patch: 54 | self.body = body 55 | default: 56 | self.body = nil 57 | } 58 | } 59 | } 60 | 61 | public enum PutioAPIErrorType { 62 | case httpError(statusCode: Int, errorType: String) 63 | case networkError 64 | case unknownError 65 | } 66 | 67 | public struct PutiopAPIErrorRequestInformation { 68 | let config: PutioAPIRequestConfig 69 | } 70 | 71 | public struct PutioAPIError: Error { 72 | public let request: PutiopAPIErrorRequestInformation 73 | public let type: PutioAPIErrorType 74 | public let message: String 75 | public let underlyingError: Error 76 | 77 | init(request: PutiopAPIErrorRequestInformation, errorJSON: JSON, error: AFError) { 78 | self.request = request 79 | self.type = .httpError(statusCode: errorJSON["status_code"].intValue, errorType: errorJSON["error_type"].stringValue) 80 | self.message = errorJSON["message"].stringValue 81 | self.underlyingError = error 82 | } 83 | 84 | init(request: PutiopAPIErrorRequestInformation, error: AFError) { 85 | self.request = request 86 | self.type = .networkError 87 | self.message = error.localizedDescription 88 | self.underlyingError = error 89 | } 90 | 91 | init(request: PutiopAPIErrorRequestInformation, error: Error) { 92 | self.request = request 93 | self.type = .unknownError 94 | self.message = error.localizedDescription 95 | self.underlyingError = error 96 | } 97 | } 98 | 99 | public typealias PutioAPIBoolCompletion = (Result) -> Void 100 | -------------------------------------------------------------------------------- /PutioAPI/Classes/PutioAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Alamofire 3 | import SwiftyJSON 4 | 5 | public protocol PutioAPIDelegate: class { 6 | func onPutioAPIApiError(error: PutioAPIError) 7 | } 8 | 9 | public final class PutioAPI { 10 | public typealias RequestCompletion = (Result) -> Void 11 | 12 | weak var delegate: PutioAPIDelegate? 13 | 14 | static let apiURL = "https://api.put.io/v2" 15 | 16 | public var config: PutioAPIConfig 17 | 18 | public init(config: PutioAPIConfig) { 19 | self.config = config 20 | } 21 | 22 | public func setToken(token: String) { 23 | self.config.token = token 24 | } 25 | 26 | public func clearToken() { 27 | self.config.token = "" 28 | } 29 | 30 | public func get(_ url: String, headers: HTTPHeaders = [:], query: Parameters = [:], _ completion: @escaping RequestCompletion) { 31 | let requestConfig = PutioAPIRequestConfig(apiConfig: config, url: url, method: .get, headers: headers, query: query) 32 | self.send(requestConfig: requestConfig, completion) 33 | } 34 | 35 | public func post(_ url: String, headers: HTTPHeaders = [:], query: Parameters = [:], body: Parameters = [:], _ completion: @escaping RequestCompletion) { 36 | let requestConfig = PutioAPIRequestConfig(apiConfig: config, url: url, method: .post, headers: headers, query: query, body: body) 37 | self.send(requestConfig: requestConfig, completion) 38 | } 39 | 40 | public func put(_ url: String, headers: HTTPHeaders = [:], query: Parameters = [:], body: Parameters = [:], _ completion: @escaping RequestCompletion) { 41 | let requestConfig = PutioAPIRequestConfig(apiConfig: config, url: url, method: .put, headers: headers, query: query, body: body) 42 | self.send(requestConfig: requestConfig, completion) 43 | } 44 | 45 | public func delete(_ url: String, headers: HTTPHeaders = [:], query: Parameters = [:], _ completion: @escaping RequestCompletion) { 46 | let requestConfig = PutioAPIRequestConfig(apiConfig: config, url: url, method: .delete, headers: headers, query: query) 47 | self.send(requestConfig: requestConfig, completion) 48 | } 49 | 50 | 51 | private func send(requestConfig: PutioAPIRequestConfig, _ completion: @escaping (Result) -> Void) { 52 | let requestInformation = PutiopAPIErrorRequestInformation(config: requestConfig) 53 | 54 | AF.request( 55 | requestConfig.url, 56 | method: requestConfig.method, 57 | parameters: requestConfig.body, 58 | encoding: JSONEncoding.default, 59 | headers: requestConfig.headers 60 | ) { $0.timeoutInterval = self.config.timeoutInterval } 61 | .validate() 62 | .responseData(completionHandler: { dataResponse in 63 | do { 64 | switch dataResponse.result { 65 | case .success(let data): 66 | let json = try JSON(data: data) 67 | return completion(.success(json)) 68 | 69 | case .failure(let error): 70 | if let data = dataResponse.data { 71 | let apiError = PutioAPIError(request: requestInformation, errorJSON: try JSON(data: data), error: error) 72 | self.delegate?.onPutioAPIApiError(error: apiError) 73 | return completion(.failure(apiError)) 74 | } 75 | 76 | let apiError = PutioAPIError(request: requestInformation, error: error) 77 | self.delegate?.onPutioAPIApiError(error: apiError) 78 | return completion(.failure(apiError)) 79 | } 80 | } catch { 81 | let apiError = PutioAPIError(request: requestInformation, error: error) 82 | self.delegate?.onPutioAPIApiError(error: apiError) 83 | return completion(.failure(apiError)) 84 | } 85 | }) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Example/PutioAPI/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/PutioAPI/ViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import PutioAPI 3 | import AuthenticationServices 4 | 5 | class ViewController: UIViewController { 6 | var api: PutioAPI? 7 | var session: ASWebAuthenticationSession? 8 | 9 | @IBOutlet weak var textField: UITextField! 10 | @IBOutlet weak var startButton: UIButton! 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | } 15 | 16 | override func viewDidAppear(_ animated: Bool) { 17 | super.viewDidAppear(animated) 18 | } 19 | 20 | @IBAction func startButtonTapped(_ sender: Any) { 21 | guard let clientID = textField.text else { return } 22 | createAPI(clientID: clientID) 23 | } 24 | 25 | func createAPI(clientID: String) { 26 | api = PutioAPI(config: PutioAPIConfig(clientID: clientID)) 27 | startAuthFlow() 28 | } 29 | 30 | // https://developer.apple.com/documentation/authenticationservices/authenticating_a_user_through_a_web_service 31 | func startAuthFlow() { 32 | guard let api = api else { return } 33 | 34 | let scheme = "putioswift" 35 | let url = api.getAuthURL(redirectURI: "\(scheme)://auth") 36 | 37 | session = ASWebAuthenticationSession(url: url, callbackURLScheme: scheme) { callbackURL, error in 38 | guard error == nil, let callbackURL = callbackURL else { 39 | return self.handleAuthCallbackFailure(error: error!) 40 | 41 | } 42 | 43 | // Callback URL: putioswift://auth#access_token={TOKEN} 44 | return self.handleAuthCallbackSuccess(callbackURL: callbackURL) 45 | } 46 | 47 | session?.presentationContextProvider = self 48 | session?.start() 49 | } 50 | 51 | func handleAuthCallbackFailure(error: Error) { 52 | let alertController = UIAlertController(title: "Auth Failure", message: error.localizedDescription, preferredStyle: .alert) 53 | let closeButton = UIAlertAction(title: "OK", style: .cancel, handler: nil) 54 | alertController.addAction(closeButton) 55 | present(alertController, animated: true, completion: nil) 56 | } 57 | 58 | func handleAuthCallbackSuccess(callbackURL: URL) { 59 | var urlComponents = URLComponents() 60 | urlComponents.query = callbackURL.fragment 61 | 62 | guard let tokenFragment = urlComponents.queryItems?.first(where: { $0.name == "access_token" }), let token = tokenFragment.value else { 63 | let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Missing access_token in the callback URL"]) 64 | return handleAuthCallbackFailure(error: error) 65 | } 66 | 67 | fetchAccountInfo(token: token) 68 | } 69 | 70 | func fetchAccountInfo(token: String) { 71 | api?.setToken(token: token) 72 | 73 | api?.getAccountInfo(completion: { result in 74 | switch result { 75 | case .success(let account): 76 | return self.fetchAccountInfoSuccess(account: account) 77 | case .failure(let error): 78 | return self.fetchAccountInfoFailure(error: error) 79 | } 80 | }) 81 | } 82 | 83 | func fetchAccountInfoFailure(error: PutioAPIError) { 84 | let alertController = UIAlertController(title: "API: Fetch Account Info Failure", message: error.message, preferredStyle: .alert) 85 | let closeButton = UIAlertAction(title: "OK", style: .cancel, handler: nil) 86 | alertController.addAction(closeButton) 87 | present(alertController, animated: true, completion: nil) 88 | } 89 | 90 | func fetchAccountInfoSuccess(account: PutioAccount) { 91 | let alertController = UIAlertController(title: "API: Fetch Account Info Success", message: account.username, preferredStyle: .alert) 92 | let closeButton = UIAlertAction(title: "OK", style: .cancel, handler: { _ in 93 | self.api?.getFiles(parentID: 0, completion: { result in 94 | switch result { 95 | case .success(let data): 96 | return print("Files result: \(data.children.count)") 97 | 98 | case .failure(let error): 99 | return print("Files error: \(error.type)") 100 | } 101 | }) 102 | }) 103 | alertController.addAction(closeButton) 104 | present(alertController, animated: true, completion: nil) 105 | } 106 | } 107 | 108 | extension ViewController: ASWebAuthenticationPresentationContextProviding { 109 | func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { 110 | return view.window ?? ASPresentationAnchor() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcodeproj/xcshareddata/xcschemes/PutioAPI-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Auth/AuthAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Alamofire 3 | 4 | extension PutioAPI { 5 | public func getAuthURL(redirectURI: String, responseType: String = "token", state: String = "") -> URL { 6 | var url = URLComponents(string: "\(self.config.baseURL)/oauth2/authenticate") 7 | 8 | url?.queryItems = [ 9 | URLQueryItem(name: "client_id", value: self.config.clientID), 10 | URLQueryItem(name: "client_name", value: self.config.clientName), 11 | URLQueryItem(name: "redirect_uri", value: redirectURI), 12 | URLQueryItem(name: "response_type", value: responseType), 13 | URLQueryItem(name: "state", value: state), 14 | ] 15 | 16 | return (url?.url!)! 17 | } 18 | 19 | public func getAuthCode(completion: @escaping (Result) -> Void) { 20 | let query = [ 21 | "app_id": self.config.clientID, 22 | "client_name": self.config.clientName.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "" 23 | ] 24 | 25 | self.get("/oauth2/oob/code", query: query) { result in 26 | switch result { 27 | case .success(let json): 28 | return completion(.success(json["code"].stringValue)) 29 | 30 | case .failure(let error): 31 | return completion(.failure(error)) 32 | } 33 | } 34 | } 35 | 36 | public func checkAuthCodeMatch(code: String, completion: @escaping (Result) -> Void) { 37 | self.get("/oauth2/oob/code/\(code)") { result in 38 | switch result { 39 | case .success(let json): 40 | return completion(.success(json["oauth_token"].stringValue)) 41 | 42 | case .failure(let error): 43 | return completion(.failure(error)) 44 | } 45 | } 46 | } 47 | 48 | public func validateToken(token: String, completion: @escaping (Result) -> Void) { 49 | self.get("/oauth2/validate") { result in 50 | switch result { 51 | case .success(let json): 52 | return completion(.success(PutioTokenValidationResult(json: json))) 53 | 54 | case .failure(let error): 55 | return completion(.failure(error)) 56 | } 57 | } 58 | } 59 | 60 | public func logout(completion: @escaping PutioAPIBoolCompletion) { 61 | self.post("/oauth/grants/logout") { result in 62 | switch result { 63 | case .success(let json): 64 | return completion(.success(json)) 65 | 66 | case .failure(let error): 67 | return completion(.failure(error)) 68 | } 69 | } 70 | } 71 | 72 | // MARK: two-factor 73 | public func generateTOTP(completion: @escaping (Result) -> Void) { 74 | self.post("/two_factor/generate/totp") { result in 75 | switch result { 76 | case .success(let json): 77 | return completion(.success(PutioGenerateTOTPResult(json: json))) 78 | case .failure(let error): 79 | return completion(.failure(error)) 80 | } 81 | } 82 | } 83 | 84 | public func verifyTOTP(code: String, completion: @escaping (Result) -> Void) { 85 | self.post("/two_factor/verify/totp", body: ["code": code]) { result in 86 | switch result { 87 | case .success(let json): 88 | return completion(.success(PutioVerifyTOTPResult(json: json))) 89 | 90 | case .failure(let error): 91 | return completion(.failure(error)) 92 | } 93 | } 94 | } 95 | 96 | public func getRecoveryCodes(completion: @escaping (Result) -> Void) { 97 | self.get("/two_factor/recovery_codes") { result in 98 | switch result { 99 | case .success(let json): 100 | return completion(.success(PutioTwoFactorRecoveryCodes(json: json["recovery_codes"]))) 101 | case .failure(let error): 102 | return completion(.failure(error)) 103 | } 104 | } 105 | } 106 | 107 | public func regenerateRecoveryCodes(completion: @escaping (Result) -> Void) { 108 | self.post("/two_factor/recovery_codes/refresh") { result in 109 | switch result { 110 | case .success(let json): 111 | return completion(.success(PutioTwoFactorRecoveryCodes(json: json["recovery_codes"]))) 112 | case .failure(let error): 113 | return completion(.failure(error)) 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/FilesModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import SwiftyJSON 3 | 4 | public enum PutioFileType { 5 | case folder, video, audio, image, pdf, other 6 | } 7 | 8 | open class PutioBaseFile { 9 | open var id: Int 10 | open var name: String 11 | open var icon: String 12 | open var type: PutioFileType 13 | open var parentID: Int 14 | open var size: Int64 15 | open var createdAt: Date 16 | 17 | init(json: JSON) { 18 | self.id = json["id"].intValue 19 | self.name = json["name"].stringValue 20 | self.icon = json["icon"].stringValue 21 | self.parentID = json["parent_id"].intValue 22 | 23 | switch json["file_type"].stringValue { 24 | case "FOLDER": 25 | self.type = .folder 26 | case "VIDEO": 27 | self.type = .video 28 | case "AUDIO": 29 | self.type = .audio 30 | case "IMAGE": 31 | self.type = .image 32 | case "PDF": 33 | self.type = .pdf 34 | default: 35 | self.type = .other 36 | } 37 | 38 | // Eg: 1024.0 39 | self.size = json["size"].int64Value 40 | 41 | // Put.io API currently does not provide dates compatible with iso8601 but may support in the future 42 | let formatter = ISO8601DateFormatter() 43 | self.createdAt = formatter.date(from: json["created_at"].stringValue) ?? formatter.date(from: "\(json["created_at"].stringValue)+00:00")! 44 | } 45 | } 46 | 47 | public struct PutioVideoMetadata { 48 | public var height: Int 49 | public var width: Int 50 | public var codec: String 51 | public var duration: Double 52 | public var aspectRatio: Double 53 | 54 | init(json: JSON) { 55 | self.height = json["height"].intValue 56 | self.width = json["width"].intValue 57 | self.codec = json["codec"].stringValue 58 | self.duration = json["duration"].doubleValue 59 | self.aspectRatio = json["aspect_ratio"].doubleValue 60 | } 61 | } 62 | 63 | open class PutioFile: PutioBaseFile { 64 | open var isShared: Bool 65 | open var updatedAt: Date 66 | 67 | // MARK: Folder Properties 68 | open var isSharedRoot: Bool = false 69 | open var sortBy: String = "" 70 | 71 | // MARK: Video Properties 72 | open var metaData: PutioVideoMetadata? 73 | open var screenshot: String = "" 74 | open var startFrom: Int = 0 75 | 76 | open var needConvert: Bool = false 77 | open var hasMp4: Bool = false 78 | 79 | open var mp4Size: Int64 = 0 80 | open var mp4StreamURL: String = "" 81 | 82 | open var streamURL: String = "" 83 | 84 | override init(json: JSON) { 85 | let base = PutioBaseFile(json: json) 86 | 87 | self.isShared = json["is_shared"].boolValue 88 | 89 | // Put.io API currently does not provide dates compatible with iso8601 but may support in the future 90 | let formatter = ISO8601DateFormatter() 91 | self.updatedAt = base.id == 0 ? base.createdAt : 92 | formatter.date(from: json["updated_at"].stringValue) ?? 93 | formatter.date(from: "\(json["updated_at"].stringValue)+00:00")! 94 | 95 | if base.type == .folder { 96 | self.sortBy = json["sort_by"].stringValue 97 | self.isSharedRoot = json["folder_type"].stringValue == "SHARED_ROOT" 98 | } 99 | 100 | if base.type == .video { 101 | self.hasMp4 = json["is_mp4_available"].boolValue 102 | self.needConvert = json["need_convert"].boolValue 103 | self.streamURL = json["stream_url"].stringValue 104 | self.mp4StreamURL = json["mp4_stream_url"].stringValue 105 | self.screenshot = json["screenshot"].stringValue 106 | 107 | if (self.hasMp4) { 108 | self.mp4Size = json["mp4_size"].int64Value 109 | } 110 | 111 | if json["video_metadata"].dictionary != nil { 112 | self.metaData = PutioVideoMetadata(json: json["video_metadata"]) 113 | } 114 | } 115 | 116 | if base.type == .audio || base.type == .video { 117 | self.startFrom = json["start_from"].intValue 118 | } 119 | 120 | super.init(json: json) 121 | } 122 | 123 | public func getStreamURL(token: String) -> URL? { 124 | switch (self.type) { 125 | case .audio: 126 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/stream?oauth_token=\(token)" 127 | return URL(string: url)! 128 | case .video: 129 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/hls/media.m3u8?subtitle_key=all&oauth_token=\(token)" 130 | return URL(string: url)! 131 | default: 132 | return nil 133 | } 134 | } 135 | 136 | public func getHlsStreamURL(token: String) -> URL { 137 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/hls/media.m3u8?subtitle_key=all&oauth_token=\(token)" 138 | return URL(string: url)! 139 | } 140 | 141 | public func getAudioStreamURL(token: String) -> URL { 142 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/stream?oauth_token=\(token)" 143 | return URL(string: url)! 144 | } 145 | 146 | public func getDownloadURL(token: String) -> URL { 147 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/download?oauth_token=\(token)" 148 | return URL(string: url)! 149 | } 150 | 151 | public func getMp4DownloadURL(token: String) -> URL { 152 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/mp4/download?oauth_token=\(token)" 153 | return URL(string: url)! 154 | } 155 | } 156 | 157 | public enum PutioNextFileType: String { 158 | case video = "VIDEO", audio = "AUDIO" 159 | } 160 | 161 | open class PutioNextFile { 162 | open var id: Int 163 | open var name: String 164 | open var parentID: Int 165 | open var type: PutioNextFileType 166 | 167 | init(json: JSON, type: PutioNextFileType) { 168 | self.id = json["id"].intValue 169 | self.parentID = json["parent_id"].intValue 170 | self.name = json["name"].stringValue 171 | self.type = type 172 | } 173 | 174 | public func getStreamURL(token: String) -> URL { 175 | switch (self.type) { 176 | case .audio: 177 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/stream?oauth_token=\(token)" 178 | return URL(string: url)! 179 | case .video: 180 | let url = "\(PutioAPI.apiURL)/files/\(self.id)/hls/media.m3u8?subtitle_key=all&oauth_token=\(token)" 181 | return URL(string: url)! 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /PutioAPI/Classes/Files/FilesAPI.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import Alamofire 3 | import SwiftyJSON 4 | 5 | extension PutioAPI { 6 | public func getFiles(parentID: Int, query: Parameters = [:], completion: @escaping (Result<(parent: PutioFile, children: [PutioFile]), PutioAPIError>) -> Void) { 7 | let url = "/files/list" 8 | let query = query.merge(with: [ 9 | "parent_id": parentID, 10 | "mp4_status_parent": 1, 11 | "stream_url_parent": 1, 12 | "mp4_stream_url_parent": 1, 13 | "video_metadata_parent": 1 14 | ]) 15 | 16 | self.get(url, query: query) { result in 17 | switch result { 18 | case .success(let json): 19 | return completion(.success((parent: PutioFile(json: json["parent"]), children: json["files"].arrayValue.map {PutioFile(json: $0)}))) 20 | case .failure(let error): 21 | return completion(.failure(error)) 22 | } 23 | } 24 | } 25 | 26 | public func getFile(fileID: Int, query: Parameters = [:], completion: @escaping (Result) -> Void) { 27 | let url = "/files/\(fileID)" 28 | let query = query.merge(with: [ 29 | "mp4_size": 1, 30 | "start_from": 1, 31 | "stream_url": 1, 32 | "mp4_stream_url": 1 33 | ]) 34 | 35 | self.get(url, query: query) { result in 36 | switch result { 37 | case .success(let json): 38 | return completion(.success(PutioFile(json: json["file"]))) 39 | case .failure(let error): 40 | return completion(.failure(error)) 41 | } 42 | } 43 | } 44 | 45 | public func deleteFile(fileID: Int, completion: @escaping PutioAPIBoolCompletion) { 46 | return self.deleteFiles(fileIDs: [fileID], completion: completion) 47 | } 48 | 49 | public func deleteFiles(fileIDs: [Int], query: Parameters = [:], completion: @escaping PutioAPIBoolCompletion) { 50 | let url = "/files/delete" 51 | let query = ["skip_nonexistents": true, "skip_owner_check": false].merge(with: query) 52 | let body = ["file_ids": (fileIDs.map {String($0)}).joined(separator: ",")] 53 | 54 | self.post(url, query: query, body: body) { result in 55 | switch result { 56 | case .success(let json): 57 | return completion(.success(json)) 58 | case .failure(let error): 59 | return completion(.failure(error)) 60 | } 61 | } 62 | } 63 | 64 | public func copyFile(fileID: Int, completion: @escaping PutioAPIBoolCompletion) { 65 | return self.copyFiles(fileIDs: [fileID], completion: completion) 66 | } 67 | 68 | public func copyFiles(fileIDs: [Int], completion: @escaping PutioAPIBoolCompletion) { 69 | let url = "/files/copy-to-disk" 70 | let body = ["file_ids": (fileIDs.map {String($0)}).joined(separator: ",")] 71 | 72 | self.post(url, body: body) { result in 73 | switch result { 74 | case .success(let json): 75 | return completion(.success(json)) 76 | case .failure(let error): 77 | return completion(.failure(error)) 78 | } 79 | } 80 | } 81 | 82 | public func moveFile(fileID: Int, parentID: Int, completion: @escaping PutioAPIBoolCompletion) { 83 | return self.moveFiles(fileIDs: [fileID], parentID: parentID, completion: completion) 84 | } 85 | 86 | public func moveFiles(fileIDs: [Int], parentID: Int, completion: @escaping PutioAPIBoolCompletion) { 87 | let url = "/files/move" 88 | let body = [ 89 | "file_ids": (fileIDs.map {String($0)}).joined(separator: ","), 90 | "parent_id": parentID 91 | ] as [String: Any] 92 | 93 | self.post(url, body: body) { result in 94 | switch result { 95 | case .success(let json): 96 | return completion(.success(json)) 97 | case .failure(let error): 98 | return completion(.failure(error)) 99 | } 100 | } 101 | } 102 | 103 | public func renameFile(fileID: Int, name: String, completion: @escaping PutioAPIBoolCompletion) { 104 | let url = "/files/rename/" 105 | let body = ["file_id": fileID, "name": name] as [String: Any] 106 | 107 | self.post(url, body: body) { result in 108 | switch result { 109 | case .success(let json): 110 | return completion(.success(json)) 111 | case .failure(let error): 112 | return completion(.failure(error)) 113 | } 114 | } 115 | } 116 | 117 | public func createFolder(name: String, parentID: Int, completion: @escaping PutioAPIBoolCompletion) { 118 | let url = "/files/create-folder" 119 | let body = ["name": name, "parent_id": parentID] as [String: Any] 120 | 121 | self.post(url, body: body) { result in 122 | switch result { 123 | case .success(let json): 124 | return completion(.success(json)) 125 | case .failure(let error): 126 | return completion(.failure(error)) 127 | } 128 | } 129 | } 130 | 131 | public func findNextFile(fileID: Int, fileType: PutioNextFileType, completion: @escaping (Result) -> Void) { 132 | let url = "/files/\(fileID)/next-file" 133 | let query = ["file_type": fileType.rawValue] 134 | 135 | self.get(url, query: query) { result in 136 | switch result { 137 | case .success(let json): 138 | return completion(.success(PutioNextFile(json: json["next_file"], type: fileType))) 139 | case .failure(let error): 140 | return completion(.failure(error)) 141 | } 142 | } 143 | } 144 | 145 | public func setSortBy(fileId: Int, sortBy: String, completion: @escaping PutioAPIBoolCompletion) { 146 | let url = "/files/set-sort-by" 147 | let body = ["file_id": fileId, "sort_by": sortBy] as [String: Any] 148 | 149 | self.post(url, body: body) { result in 150 | switch result { 151 | case .success(let json): 152 | return completion(.success(json)) 153 | case .failure(let error): 154 | return completion(.failure(error)) 155 | } 156 | } 157 | } 158 | 159 | public func resetFileSpecificSortSettings(completion: @escaping PutioAPIBoolCompletion) { 160 | let url = "/files/remove-sort-by-settings" 161 | 162 | self.post(url) { result in 163 | switch result { 164 | case .success(let json): 165 | return completion(.success(json)) 166 | case .failure(let error): 167 | return completion(.failure(error)) 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /Example/PutioAPI/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/PutioAPI.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 11 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 12 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 13 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 14 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 15 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 16 | C3360688E59521A72E8278FC /* Pods_PutioAPI_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2487554597704BB13674151C /* Pods_PutioAPI_Tests.framework */; }; 17 | EC52E6B5676446D7E6E3DB30 /* Pods_PutioAPI_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E6EBAD6D4F0A6B6B250E237 /* Pods_PutioAPI_Example.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 26 | remoteInfo = PutioAPI; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 0975DE25AE7B7A22CAC8EF92 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 32 | 09E2FBC7FE74AF1ADDA23ECE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 33 | 0E6EBAD6D4F0A6B6B250E237 /* Pods_PutioAPI_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PutioAPI_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 2487554597704BB13674151C /* Pods_PutioAPI_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_PutioAPI_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | 5950505F610BBE2039DD7607 /* Pods-PutioAPI_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PutioAPI_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-PutioAPI_Tests/Pods-PutioAPI_Tests.release.xcconfig"; sourceTree = ""; }; 36 | 607FACD01AFB9204008FA782 /* PutioAPI_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PutioAPI_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 39 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 40 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 41 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 42 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 43 | 607FACE51AFB9204008FA782 /* PutioAPI_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PutioAPI_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 46 | D3F0232F530F97D8AA7C288D /* Pods-PutioAPI_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PutioAPI_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PutioAPI_Example/Pods-PutioAPI_Example.debug.xcconfig"; sourceTree = ""; }; 47 | E3A3E259B911EFC7E9FE370F /* PutioAPI.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = PutioAPI.podspec; path = ../PutioAPI.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 48 | EC74F437665BBD68DDF7602B /* Pods-PutioAPI_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PutioAPI_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PutioAPI_Tests/Pods-PutioAPI_Tests.debug.xcconfig"; sourceTree = ""; }; 49 | F6F96589FBAF9511A70C98C4 /* Pods-PutioAPI_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PutioAPI_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-PutioAPI_Example/Pods-PutioAPI_Example.release.xcconfig"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | EC52E6B5676446D7E6E3DB30 /* Pods_PutioAPI_Example.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | C3360688E59521A72E8278FC /* Pods_PutioAPI_Tests.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 607FACC71AFB9204008FA782 = { 73 | isa = PBXGroup; 74 | children = ( 75 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 76 | 607FACD21AFB9204008FA782 /* Example for PutioAPI */, 77 | 607FACE81AFB9204008FA782 /* Tests */, 78 | 607FACD11AFB9204008FA782 /* Products */, 79 | 60DD32477A42D2FFE53706A5 /* Pods */, 80 | 7DCE53FA42886BCD468D0516 /* Frameworks */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 607FACD11AFB9204008FA782 /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 607FACD01AFB9204008FA782 /* PutioAPI_Example.app */, 88 | 607FACE51AFB9204008FA782 /* PutioAPI_Tests.xctest */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 607FACD21AFB9204008FA782 /* Example for PutioAPI */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 97 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 98 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 99 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 100 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 101 | 607FACD31AFB9204008FA782 /* Supporting Files */, 102 | ); 103 | name = "Example for PutioAPI"; 104 | path = PutioAPI; 105 | sourceTree = ""; 106 | }; 107 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 607FACD41AFB9204008FA782 /* Info.plist */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | 607FACE81AFB9204008FA782 /* Tests */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 119 | 607FACE91AFB9204008FA782 /* Supporting Files */, 120 | ); 121 | path = Tests; 122 | sourceTree = ""; 123 | }; 124 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 607FACEA1AFB9204008FA782 /* Info.plist */, 128 | ); 129 | name = "Supporting Files"; 130 | sourceTree = ""; 131 | }; 132 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | E3A3E259B911EFC7E9FE370F /* PutioAPI.podspec */, 136 | 09E2FBC7FE74AF1ADDA23ECE /* README.md */, 137 | 0975DE25AE7B7A22CAC8EF92 /* LICENSE */, 138 | ); 139 | name = "Podspec Metadata"; 140 | sourceTree = ""; 141 | }; 142 | 60DD32477A42D2FFE53706A5 /* Pods */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | D3F0232F530F97D8AA7C288D /* Pods-PutioAPI_Example.debug.xcconfig */, 146 | F6F96589FBAF9511A70C98C4 /* Pods-PutioAPI_Example.release.xcconfig */, 147 | EC74F437665BBD68DDF7602B /* Pods-PutioAPI_Tests.debug.xcconfig */, 148 | 5950505F610BBE2039DD7607 /* Pods-PutioAPI_Tests.release.xcconfig */, 149 | ); 150 | name = Pods; 151 | sourceTree = ""; 152 | }; 153 | 7DCE53FA42886BCD468D0516 /* Frameworks */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 0E6EBAD6D4F0A6B6B250E237 /* Pods_PutioAPI_Example.framework */, 157 | 2487554597704BB13674151C /* Pods_PutioAPI_Tests.framework */, 158 | ); 159 | name = Frameworks; 160 | sourceTree = ""; 161 | }; 162 | /* End PBXGroup section */ 163 | 164 | /* Begin PBXNativeTarget section */ 165 | 607FACCF1AFB9204008FA782 /* PutioAPI_Example */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "PutioAPI_Example" */; 168 | buildPhases = ( 169 | 33E0B92A0DFF049029A0F70F /* [CP] Check Pods Manifest.lock */, 170 | 607FACCC1AFB9204008FA782 /* Sources */, 171 | 607FACCD1AFB9204008FA782 /* Frameworks */, 172 | 607FACCE1AFB9204008FA782 /* Resources */, 173 | 01B23D3CAFC9C30360593AD1 /* [CP] Embed Pods Frameworks */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = PutioAPI_Example; 180 | productName = PutioAPI; 181 | productReference = 607FACD01AFB9204008FA782 /* PutioAPI_Example.app */; 182 | productType = "com.apple.product-type.application"; 183 | }; 184 | 607FACE41AFB9204008FA782 /* PutioAPI_Tests */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "PutioAPI_Tests" */; 187 | buildPhases = ( 188 | 4DDA3A94927489A4CA1F5462 /* [CP] Check Pods Manifest.lock */, 189 | 607FACE11AFB9204008FA782 /* Sources */, 190 | 607FACE21AFB9204008FA782 /* Frameworks */, 191 | 607FACE31AFB9204008FA782 /* Resources */, 192 | ); 193 | buildRules = ( 194 | ); 195 | dependencies = ( 196 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 197 | ); 198 | name = PutioAPI_Tests; 199 | productName = Tests; 200 | productReference = 607FACE51AFB9204008FA782 /* PutioAPI_Tests.xctest */; 201 | productType = "com.apple.product-type.bundle.unit-test"; 202 | }; 203 | /* End PBXNativeTarget section */ 204 | 205 | /* Begin PBXProject section */ 206 | 607FACC81AFB9204008FA782 /* Project object */ = { 207 | isa = PBXProject; 208 | attributes = { 209 | LastSwiftUpdateCheck = 0830; 210 | LastUpgradeCheck = 1010; 211 | ORGANIZATIONNAME = CocoaPods; 212 | TargetAttributes = { 213 | 607FACCF1AFB9204008FA782 = { 214 | CreatedOnToolsVersion = 6.3.1; 215 | DevelopmentTeam = STJ42L5PJH; 216 | LastSwiftMigration = 1010; 217 | }; 218 | 607FACE41AFB9204008FA782 = { 219 | CreatedOnToolsVersion = 6.3.1; 220 | DevelopmentTeam = STJ42L5PJH; 221 | LastSwiftMigration = 1010; 222 | TestTargetID = 607FACCF1AFB9204008FA782; 223 | }; 224 | }; 225 | }; 226 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "PutioAPI" */; 227 | compatibilityVersion = "Xcode 3.2"; 228 | developmentRegion = English; 229 | hasScannedForEncodings = 0; 230 | knownRegions = ( 231 | English, 232 | en, 233 | Base, 234 | ); 235 | mainGroup = 607FACC71AFB9204008FA782; 236 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 237 | projectDirPath = ""; 238 | projectRoot = ""; 239 | targets = ( 240 | 607FACCF1AFB9204008FA782 /* PutioAPI_Example */, 241 | 607FACE41AFB9204008FA782 /* PutioAPI_Tests */, 242 | ); 243 | }; 244 | /* End PBXProject section */ 245 | 246 | /* Begin PBXResourcesBuildPhase section */ 247 | 607FACCE1AFB9204008FA782 /* Resources */ = { 248 | isa = PBXResourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 252 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 253 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 607FACE31AFB9204008FA782 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXResourcesBuildPhase section */ 265 | 266 | /* Begin PBXShellScriptBuildPhase section */ 267 | 01B23D3CAFC9C30360593AD1 /* [CP] Embed Pods Frameworks */ = { 268 | isa = PBXShellScriptBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | inputPaths = ( 273 | "${PODS_ROOT}/Target Support Files/Pods-PutioAPI_Example/Pods-PutioAPI_Example-frameworks.sh", 274 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", 275 | "${BUILT_PRODUCTS_DIR}/PutioAPI/PutioAPI.framework", 276 | "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", 277 | ); 278 | name = "[CP] Embed Pods Frameworks"; 279 | outputPaths = ( 280 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", 281 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PutioAPI.framework", 282 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | shellPath = /bin/sh; 286 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PutioAPI_Example/Pods-PutioAPI_Example-frameworks.sh\"\n"; 287 | showEnvVarsInLog = 0; 288 | }; 289 | 33E0B92A0DFF049029A0F70F /* [CP] Check Pods Manifest.lock */ = { 290 | isa = PBXShellScriptBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | ); 294 | inputFileListPaths = ( 295 | ); 296 | inputPaths = ( 297 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 298 | "${PODS_ROOT}/Manifest.lock", 299 | ); 300 | name = "[CP] Check Pods Manifest.lock"; 301 | outputFileListPaths = ( 302 | ); 303 | outputPaths = ( 304 | "$(DERIVED_FILE_DIR)/Pods-PutioAPI_Example-checkManifestLockResult.txt", 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | shellPath = /bin/sh; 308 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 309 | showEnvVarsInLog = 0; 310 | }; 311 | 4DDA3A94927489A4CA1F5462 /* [CP] Check Pods Manifest.lock */ = { 312 | isa = PBXShellScriptBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputFileListPaths = ( 317 | ); 318 | inputPaths = ( 319 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 320 | "${PODS_ROOT}/Manifest.lock", 321 | ); 322 | name = "[CP] Check Pods Manifest.lock"; 323 | outputFileListPaths = ( 324 | ); 325 | outputPaths = ( 326 | "$(DERIVED_FILE_DIR)/Pods-PutioAPI_Tests-checkManifestLockResult.txt", 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | shellPath = /bin/sh; 330 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 331 | showEnvVarsInLog = 0; 332 | }; 333 | /* End PBXShellScriptBuildPhase section */ 334 | 335 | /* Begin PBXSourcesBuildPhase section */ 336 | 607FACCC1AFB9204008FA782 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 341 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | 607FACE11AFB9204008FA782 /* Sources */ = { 346 | isa = PBXSourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | /* End PBXSourcesBuildPhase section */ 354 | 355 | /* Begin PBXTargetDependency section */ 356 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 357 | isa = PBXTargetDependency; 358 | target = 607FACCF1AFB9204008FA782 /* PutioAPI_Example */; 359 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 360 | }; 361 | /* End PBXTargetDependency section */ 362 | 363 | /* Begin PBXVariantGroup section */ 364 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 365 | isa = PBXVariantGroup; 366 | children = ( 367 | 607FACDA1AFB9204008FA782 /* Base */, 368 | ); 369 | name = Main.storyboard; 370 | sourceTree = ""; 371 | }; 372 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 373 | isa = PBXVariantGroup; 374 | children = ( 375 | 607FACDF1AFB9204008FA782 /* Base */, 376 | ); 377 | name = LaunchScreen.xib; 378 | sourceTree = ""; 379 | }; 380 | /* End PBXVariantGroup section */ 381 | 382 | /* Begin XCBuildConfiguration section */ 383 | 607FACED1AFB9204008FA782 /* Debug */ = { 384 | isa = XCBuildConfiguration; 385 | buildSettings = { 386 | ALWAYS_SEARCH_USER_PATHS = NO; 387 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 388 | CLANG_CXX_LIBRARY = "libc++"; 389 | CLANG_ENABLE_MODULES = YES; 390 | CLANG_ENABLE_OBJC_ARC = YES; 391 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 392 | CLANG_WARN_BOOL_CONVERSION = YES; 393 | CLANG_WARN_COMMA = YES; 394 | CLANG_WARN_CONSTANT_CONVERSION = YES; 395 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 396 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 397 | CLANG_WARN_EMPTY_BODY = YES; 398 | CLANG_WARN_ENUM_CONVERSION = YES; 399 | CLANG_WARN_INFINITE_RECURSION = YES; 400 | CLANG_WARN_INT_CONVERSION = YES; 401 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 402 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 403 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 404 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 405 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 406 | CLANG_WARN_STRICT_PROTOTYPES = YES; 407 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 408 | CLANG_WARN_UNREACHABLE_CODE = YES; 409 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 410 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 411 | COPY_PHASE_STRIP = NO; 412 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 413 | ENABLE_STRICT_OBJC_MSGSEND = YES; 414 | ENABLE_TESTABILITY = YES; 415 | GCC_C_LANGUAGE_STANDARD = gnu99; 416 | GCC_DYNAMIC_NO_PIC = NO; 417 | GCC_NO_COMMON_BLOCKS = YES; 418 | GCC_OPTIMIZATION_LEVEL = 0; 419 | GCC_PREPROCESSOR_DEFINITIONS = ( 420 | "DEBUG=1", 421 | "$(inherited)", 422 | ); 423 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 426 | GCC_WARN_UNDECLARED_SELECTOR = YES; 427 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 428 | GCC_WARN_UNUSED_FUNCTION = YES; 429 | GCC_WARN_UNUSED_VARIABLE = YES; 430 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 431 | MTL_ENABLE_DEBUG_INFO = YES; 432 | ONLY_ACTIVE_ARCH = YES; 433 | SDKROOT = iphoneos; 434 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 435 | }; 436 | name = Debug; 437 | }; 438 | 607FACEE1AFB9204008FA782 /* Release */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ALWAYS_SEARCH_USER_PATHS = NO; 442 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 443 | CLANG_CXX_LIBRARY = "libc++"; 444 | CLANG_ENABLE_MODULES = YES; 445 | CLANG_ENABLE_OBJC_ARC = YES; 446 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 447 | CLANG_WARN_BOOL_CONVERSION = YES; 448 | CLANG_WARN_COMMA = YES; 449 | CLANG_WARN_CONSTANT_CONVERSION = YES; 450 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 451 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 452 | CLANG_WARN_EMPTY_BODY = YES; 453 | CLANG_WARN_ENUM_CONVERSION = YES; 454 | CLANG_WARN_INFINITE_RECURSION = YES; 455 | CLANG_WARN_INT_CONVERSION = YES; 456 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 457 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 458 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 461 | CLANG_WARN_STRICT_PROTOTYPES = YES; 462 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 463 | CLANG_WARN_UNREACHABLE_CODE = YES; 464 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 465 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 466 | COPY_PHASE_STRIP = NO; 467 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 468 | ENABLE_NS_ASSERTIONS = NO; 469 | ENABLE_STRICT_OBJC_MSGSEND = YES; 470 | GCC_C_LANGUAGE_STANDARD = gnu99; 471 | GCC_NO_COMMON_BLOCKS = YES; 472 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 473 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 474 | GCC_WARN_UNDECLARED_SELECTOR = YES; 475 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 476 | GCC_WARN_UNUSED_FUNCTION = YES; 477 | GCC_WARN_UNUSED_VARIABLE = YES; 478 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 479 | MTL_ENABLE_DEBUG_INFO = NO; 480 | SDKROOT = iphoneos; 481 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 482 | VALIDATE_PRODUCT = YES; 483 | }; 484 | name = Release; 485 | }; 486 | 607FACF01AFB9204008FA782 /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | baseConfigurationReference = D3F0232F530F97D8AA7C288D /* Pods-PutioAPI_Example.debug.xcconfig */; 489 | buildSettings = { 490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 491 | DEVELOPMENT_TEAM = STJ42L5PJH; 492 | INFOPLIST_FILE = PutioAPI/Info.plist; 493 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 494 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 495 | MODULE_NAME = ExampleApp; 496 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 497 | PRODUCT_NAME = "$(TARGET_NAME)"; 498 | SWIFT_VERSION = 4.2; 499 | }; 500 | name = Debug; 501 | }; 502 | 607FACF11AFB9204008FA782 /* Release */ = { 503 | isa = XCBuildConfiguration; 504 | baseConfigurationReference = F6F96589FBAF9511A70C98C4 /* Pods-PutioAPI_Example.release.xcconfig */; 505 | buildSettings = { 506 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 507 | DEVELOPMENT_TEAM = STJ42L5PJH; 508 | INFOPLIST_FILE = PutioAPI/Info.plist; 509 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 510 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 511 | MODULE_NAME = ExampleApp; 512 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | SWIFT_VERSION = 4.2; 515 | }; 516 | name = Release; 517 | }; 518 | 607FACF31AFB9204008FA782 /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | baseConfigurationReference = EC74F437665BBD68DDF7602B /* Pods-PutioAPI_Tests.debug.xcconfig */; 521 | buildSettings = { 522 | DEVELOPMENT_TEAM = STJ42L5PJH; 523 | FRAMEWORK_SEARCH_PATHS = ( 524 | "$(SDKROOT)/Developer/Library/Frameworks", 525 | "$(inherited)", 526 | ); 527 | GCC_PREPROCESSOR_DEFINITIONS = ( 528 | "DEBUG=1", 529 | "$(inherited)", 530 | ); 531 | INFOPLIST_FILE = Tests/Info.plist; 532 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | SWIFT_VERSION = 4.2; 537 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PutioAPI_Example.app/PutioAPI_Example"; 538 | }; 539 | name = Debug; 540 | }; 541 | 607FACF41AFB9204008FA782 /* Release */ = { 542 | isa = XCBuildConfiguration; 543 | baseConfigurationReference = 5950505F610BBE2039DD7607 /* Pods-PutioAPI_Tests.release.xcconfig */; 544 | buildSettings = { 545 | DEVELOPMENT_TEAM = STJ42L5PJH; 546 | FRAMEWORK_SEARCH_PATHS = ( 547 | "$(SDKROOT)/Developer/Library/Frameworks", 548 | "$(inherited)", 549 | ); 550 | INFOPLIST_FILE = Tests/Info.plist; 551 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 552 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 553 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_VERSION = 4.2; 556 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PutioAPI_Example.app/PutioAPI_Example"; 557 | }; 558 | name = Release; 559 | }; 560 | /* End XCBuildConfiguration section */ 561 | 562 | /* Begin XCConfigurationList section */ 563 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "PutioAPI" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 607FACED1AFB9204008FA782 /* Debug */, 567 | 607FACEE1AFB9204008FA782 /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "PutioAPI_Example" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 607FACF01AFB9204008FA782 /* Debug */, 576 | 607FACF11AFB9204008FA782 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "PutioAPI_Tests" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 607FACF31AFB9204008FA782 /* Debug */, 585 | 607FACF41AFB9204008FA782 /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | /* End XCConfigurationList section */ 591 | }; 592 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 593 | } 594 | --------------------------------------------------------------------------------