├── .gitignore
├── .swift-version
├── .travis.yml
├── CHANGELOG.md
├── GithubPilot.podspec
├── GithubPilot.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ ├── GithubPilot.xcscheme
│ └── GithubPilotTests.xcscheme
├── GithubPilot.xcworkspace
└── contents.xcworkspacedata
├── GithubPilot
├── GithubPilot.h
├── Info.plist
└── README.md
├── GithubPilotTests
├── FakeData
│ ├── repo.json
│ ├── user_me.json
│ ├── user_mietzmithut.json
│ ├── user_repos.json
│ └── users.json
├── GithubPilotTests-Bridging-Header.h
├── GithubPilotTests.swift
├── Info.plist
├── StarGazersTest.swift
├── TestHelper.swift
└── UserTests.swift
├── LICENCE
├── Podfile
├── Podfile.lock
├── Pods
├── Alamofire
│ ├── LICENSE
│ ├── README.md
│ └── Source
│ │ ├── AFError.swift
│ │ ├── Alamofire.swift
│ │ ├── DispatchQueue+Alamofire.swift
│ │ ├── MultipartFormData.swift
│ │ ├── NetworkReachabilityManager.swift
│ │ ├── Notifications.swift
│ │ ├── ParameterEncoding.swift
│ │ ├── Request.swift
│ │ ├── Response.swift
│ │ ├── ResponseSerialization.swift
│ │ ├── Result.swift
│ │ ├── ServerTrustPolicy.swift
│ │ ├── SessionDelegate.swift
│ │ ├── SessionManager.swift
│ │ ├── TaskDelegate.swift
│ │ ├── Timeline.swift
│ │ └── Validation.swift
├── Manifest.lock
├── Nocilla
│ ├── LICENSE
│ ├── Nocilla
│ │ ├── Categories
│ │ │ ├── NSData+Nocilla.h
│ │ │ ├── NSData+Nocilla.m
│ │ │ ├── NSString+Nocilla.h
│ │ │ └── NSString+Nocilla.m
│ │ ├── DSL
│ │ │ ├── LSHTTPRequestDSLRepresentation.h
│ │ │ ├── LSHTTPRequestDSLRepresentation.m
│ │ │ ├── LSStubRequestDSL.h
│ │ │ ├── LSStubRequestDSL.m
│ │ │ ├── LSStubResponseDSL.h
│ │ │ └── LSStubResponseDSL.m
│ │ ├── Diff
│ │ │ ├── LSHTTPRequestDiff.h
│ │ │ └── LSHTTPRequestDiff.m
│ │ ├── Hooks
│ │ │ ├── ASIHTTPRequest
│ │ │ │ ├── ASIHTTPRequestStub.h
│ │ │ │ ├── ASIHTTPRequestStub.m
│ │ │ │ ├── LSASIHTTPRequestAdapter.h
│ │ │ │ ├── LSASIHTTPRequestAdapter.m
│ │ │ │ ├── LSASIHTTPRequestHook.h
│ │ │ │ └── LSASIHTTPRequestHook.m
│ │ │ ├── LSHTTPClientHook.h
│ │ │ ├── LSHTTPClientHook.m
│ │ │ ├── NSURLRequest
│ │ │ │ ├── LSHTTPStubURLProtocol.h
│ │ │ │ ├── LSHTTPStubURLProtocol.m
│ │ │ │ ├── LSNSURLHook.h
│ │ │ │ ├── LSNSURLHook.m
│ │ │ │ ├── NSURLRequest+DSL.h
│ │ │ │ ├── NSURLRequest+DSL.m
│ │ │ │ ├── NSURLRequest+LSHTTPRequest.h
│ │ │ │ └── NSURLRequest+LSHTTPRequest.m
│ │ │ └── NSURLSession
│ │ │ │ ├── LSNSURLSessionHook.h
│ │ │ │ └── LSNSURLSessionHook.m
│ │ ├── LSNocilla.h
│ │ ├── LSNocilla.m
│ │ ├── Matchers
│ │ │ ├── LSDataMatcher.h
│ │ │ ├── LSDataMatcher.m
│ │ │ ├── LSMatcheable.h
│ │ │ ├── LSMatcher.h
│ │ │ ├── LSMatcher.m
│ │ │ ├── LSRegexMatcher.h
│ │ │ ├── LSRegexMatcher.m
│ │ │ ├── LSStringMatcher.h
│ │ │ ├── LSStringMatcher.m
│ │ │ ├── NSData+Matcheable.h
│ │ │ ├── NSData+Matcheable.m
│ │ │ ├── NSRegularExpression+Matcheable.h
│ │ │ ├── NSRegularExpression+Matcheable.m
│ │ │ ├── NSString+Matcheable.h
│ │ │ └── NSString+Matcheable.m
│ │ ├── Model
│ │ │ ├── LSHTTPBody.h
│ │ │ ├── LSHTTPRequest.h
│ │ │ └── LSHTTPResponse.h
│ │ ├── Nocilla.h
│ │ └── Stubs
│ │ │ ├── LSStubRequest.h
│ │ │ ├── LSStubRequest.m
│ │ │ ├── LSStubResponse.h
│ │ │ └── LSStubResponse.m
│ └── README.md
├── Pods.xcodeproj
│ └── project.pbxproj
└── Target Support Files
│ ├── Alamofire
│ ├── Alamofire-dummy.m
│ ├── Alamofire-prefix.pch
│ ├── Alamofire-umbrella.h
│ ├── Alamofire.modulemap
│ ├── Alamofire.xcconfig
│ └── Info.plist
│ ├── Nocilla
│ ├── Info.plist
│ ├── Nocilla-dummy.m
│ ├── Nocilla-prefix.pch
│ ├── Nocilla-umbrella.h
│ ├── Nocilla.modulemap
│ └── Nocilla.xcconfig
│ ├── Pods-GithubPilot
│ ├── Info.plist
│ ├── Pods-GithubPilot-acknowledgements.markdown
│ ├── Pods-GithubPilot-acknowledgements.plist
│ ├── Pods-GithubPilot-dummy.m
│ ├── Pods-GithubPilot-resources.sh
│ ├── Pods-GithubPilot-umbrella.h
│ ├── Pods-GithubPilot.debug.xcconfig
│ ├── Pods-GithubPilot.modulemap
│ └── Pods-GithubPilot.release.xcconfig
│ └── Pods-GithubPilotTests
│ ├── Info.plist
│ ├── Pods-GithubPilotTests-acknowledgements.markdown
│ ├── Pods-GithubPilotTests-acknowledgements.plist
│ ├── Pods-GithubPilotTests-dummy.m
│ ├── Pods-GithubPilotTests-frameworks.sh
│ ├── Pods-GithubPilotTests-resources.sh
│ ├── Pods-GithubPilotTests-umbrella.h
│ ├── Pods-GithubPilotTests.debug.xcconfig
│ ├── Pods-GithubPilotTests.modulemap
│ └── Pods-GithubPilotTests.release.xcconfig
├── README.md
└── Sources
├── Constants.swift
├── Github.swift
├── GithubClient.swift
├── GithubRequest.swift
├── GithubSerializers.swift
├── Helpers
├── Character+Extensions.swift
├── Dictionray+Extensions.swift
└── Helper.swift
├── Models
├── GithubEvent.swift
├── GithubRepo.swift
└── GithubUser.swift
├── OAuth.swift
└── Routes
├── AuthenticationRoutes.swift
├── EventsRoutes.swift
├── ReposRoutes.swift
├── SearchRoutes.swift
├── StarsRoutes.swift
└── UsersRoutes.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 | *.xcscmblueprint
25 |
26 | ## Obj-C/Swift specific
27 | *.hmap
28 | *.ipa
29 |
30 | ## Playgrounds
31 | timeline.xctimeline
32 | playground.xcworkspace
33 |
34 | # Swift Package Manager
35 | #
36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
37 | # Packages/
38 | .build/
39 |
40 | # CocoaPods
41 | #
42 | # We recommend against adding the Pods directory to your .gitignore. However
43 | # you should judge for yourself, the pros and cons are mentioned at:
44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
45 | #
46 | # Pods/
47 |
48 | # Carthage
49 | #
50 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
51 | # Carthage/Checkouts
52 |
53 | Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Reference: https://github.com/pinterest/PINRemoteImage/blob/master/.travis.yml
2 | # https://github.com/Alamofire/Alamofire/blob/master/.travis.yml
3 | language: objective-c
4 | osx_image: xcode8
5 | env:
6 | global:
7 | - LC_CTYPE=en_US.UTF-8
8 | - LANG=en_US.UTF-8
9 | matrix:
10 | - SCHEME="GithubPilot"
11 | - IOS_SDK=iphonesimulator
12 | - DESTINATION="platform=iOS Simulator,name=iPhone 7, OS=latest"
13 |
14 | before_install:
15 | - gem install cocoapods --no-rdoc --no-ri --no-document --quiet
16 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
17 |
18 | script:
19 | - set -o pipefail
20 | - xcodebuild -version
21 | - xctool -workspace GithubPilot.xcworkspace -scheme "$SCHEME" -sdk "$IOS_SDK" -destination "$DESTINATION" ONLY_ACTIVE_ARCH=NO CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
22 | # - xctool test -workspace GithubPilot.xcworkspace -scheme GithubPilotTests -sdk iphonesimulator9.2 ONLY_ACTIVE_ARCH=NO
23 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.0.3 - GithubPilot, Brand New](https://github.com/jindulys/GithubPilot/releases/tag/1.0.2) (2016-03-25)
2 |
3 | #### Add
4 | * Search API
5 |
6 |
7 | ## [1.0.2 - GithubPilot, Brand New](https://github.com/jindulys/GithubPilot/releases/tag/1.0.2) (2016-03-15)
8 |
9 | #### Add
10 | * Fetch full Information for an Array of API Users.
11 |
12 | #### Fix
13 | * Got rid of `precondition` to avoid crashing from SDK.
14 | * Optimized the use of Structure.
15 |
--------------------------------------------------------------------------------
/GithubPilot.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.platform = :ios
3 | s.ios.deployment_target = '9.0'
4 | s.name = "GithubPilot"
5 | s.version = "1.1.11"
6 | s.summary = "Github API V3 Swifty Wrapper"
7 | s.description = <<-DESC
8 | A swift implementaion of Github API V3, make query to Github easier.
9 | DESC
10 |
11 | s.homepage = "https://github.com/jindulys/GithubPilot"
12 | s.license = { :type => "MIT", :file => "LICENCE" }
13 |
14 |
15 | s.author = { "yansong li" => "liyansong.edw@gmail.com" }
16 |
17 | s.source = { :git => "https://github.com/jindulys/GithubPilot.git", :tag => s.version }
18 |
19 | s.source_files = "Sources/**/*.*"
20 | s.requires_arc = true
21 | s.dependency 'Alamofire', '~> 4.0.0'
22 | s.ios.framework = "UIKit"
23 | end
24 |
--------------------------------------------------------------------------------
/GithubPilot.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/GithubPilot.xcodeproj/xcshareddata/xcschemes/GithubPilot.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/GithubPilot.xcodeproj/xcshareddata/xcschemes/GithubPilotTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
16 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
43 |
49 |
50 |
52 |
53 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/GithubPilot.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/GithubPilot/GithubPilot.h:
--------------------------------------------------------------------------------
1 | //
2 | // GithubPilot.h
3 | // GithubPilot
4 | //
5 | // Created by yansong li on 2016-02-20.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for GithubPilot.
12 | FOUNDATION_EXPORT double GithubPilotVersionNumber;
13 |
14 | //! Project version string for GithubPilot.
15 | FOUNDATION_EXPORT const unsigned char GithubPilotVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/GithubPilot/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | NSAppTransportSecurity
12 |
13 | NSAllowsArbitraryLoads
14 |
15 |
16 | CFBundleInfoDictionaryVersion
17 | 6.0
18 | CFBundleName
19 | $(PRODUCT_NAME)
20 | CFBundlePackageType
21 | FMWK
22 | CFBundleShortVersionString
23 | 1.0
24 | CFBundleSignature
25 | ????
26 | CFBundleVersion
27 | $(CURRENT_PROJECT_VERSION)
28 | NSPrincipalClass
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/GithubPilot/README.md:
--------------------------------------------------------------------------------
1 | # GithubPilot
2 | Github API V3 Swifty Wrapper
3 |
4 | To install
5 |
6 | pod 'GithubPilot', '~>0.0.1'
7 |
8 | # IN PROGRESS, STAY TUNED
9 |
--------------------------------------------------------------------------------
/GithubPilotTests/FakeData/repo.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": 10824973,
3 | "name": "Test",
4 | "full_name": "mietzmithut/Test",
5 | "owner": {
6 | "login": "mietzmithut",
7 | "id": 4672699,
8 | "avatar_url": "https://avatars.githubusercontent.com/u/4672699?v=3",
9 | "gravatar_id": "",
10 | "url": "https://api.github.com/users/mietzmithut",
11 | "html_url": "https://github.com/mietzmithut",
12 | "followers_url": "https://api.github.com/users/mietzmithut/followers",
13 | "following_url": "https://api.github.com/users/mietzmithut/following{/other_user}",
14 | "gists_url": "https://api.github.com/users/mietzmithut/gists{/gist_id}",
15 | "starred_url": "https://api.github.com/users/mietzmithut/starred{/owner}{/repo}",
16 | "subscriptions_url": "https://api.github.com/users/mietzmithut/subscriptions",
17 | "organizations_url": "https://api.github.com/users/mietzmithut/orgs",
18 | "repos_url": "https://api.github.com/users/mietzmithut/repos",
19 | "events_url": "https://api.github.com/users/mietzmithut/events{/privacy}",
20 | "received_events_url": "https://api.github.com/users/mietzmithut/received_events",
21 | "type": "User",
22 | "site_admin": false
23 | },
24 | "private": false,
25 | "html_url": "https://github.com/mietzmithut/Test",
26 | "description": "",
27 | "fork": false,
28 | "url": "https://api.github.com/repos/mietzmithut/Test",
29 | "forks_url": "https://api.github.com/repos/mietzmithut/Test/forks",
30 | "keys_url": "https://api.github.com/repos/mietzmithut/Test/keys{/key_id}",
31 | "collaborators_url": "https://api.github.com/repos/mietzmithut/Test/collaborators{/collaborator}",
32 | "teams_url": "https://api.github.com/repos/mietzmithut/Test/teams",
33 | "hooks_url": "https://api.github.com/repos/mietzmithut/Test/hooks",
34 | "issue_events_url": "https://api.github.com/repos/mietzmithut/Test/issues/events{/number}",
35 | "events_url": "https://api.github.com/repos/mietzmithut/Test/events",
36 | "assignees_url": "https://api.github.com/repos/mietzmithut/Test/assignees{/user}",
37 | "branches_url": "https://api.github.com/repos/mietzmithut/Test/branches{/branch}",
38 | "tags_url": "https://api.github.com/repos/mietzmithut/Test/tags",
39 | "blobs_url": "https://api.github.com/repos/mietzmithut/Test/git/blobs{/sha}",
40 | "git_tags_url": "https://api.github.com/repos/mietzmithut/Test/git/tags{/sha}",
41 | "git_refs_url": "https://api.github.com/repos/mietzmithut/Test/git/refs{/sha}",
42 | "trees_url": "https://api.github.com/repos/mietzmithut/Test/git/trees{/sha}",
43 | "statuses_url": "https://api.github.com/repos/mietzmithut/Test/statuses/{sha}",
44 | "languages_url": "https://api.github.com/repos/mietzmithut/Test/languages",
45 | "stargazers_url": "https://api.github.com/repos/mietzmithut/Test/stargazers",
46 | "contributors_url": "https://api.github.com/repos/mietzmithut/Test/contributors",
47 | "subscribers_url": "https://api.github.com/repos/mietzmithut/Test/subscribers",
48 | "subscription_url": "https://api.github.com/repos/mietzmithut/Test/subscription",
49 | "commits_url": "https://api.github.com/repos/mietzmithut/Test/commits{/sha}",
50 | "git_commits_url": "https://api.github.com/repos/mietzmithut/Test/git/commits{/sha}",
51 | "comments_url": "https://api.github.com/repos/mietzmithut/Test/comments{/number}",
52 | "issue_comment_url": "https://api.github.com/repos/mietzmithut/Test/issues/comments/{number}",
53 | "contents_url": "https://api.github.com/repos/mietzmithut/Test/contents/{+path}",
54 | "compare_url": "https://api.github.com/repos/mietzmithut/Test/compare/{base}...{head}",
55 | "merges_url": "https://api.github.com/repos/mietzmithut/Test/merges",
56 | "archive_url": "https://api.github.com/repos/mietzmithut/Test/{archive_format}{/ref}",
57 | "downloads_url": "https://api.github.com/repos/mietzmithut/Test/downloads",
58 | "issues_url": "https://api.github.com/repos/mietzmithut/Test/issues{/number}",
59 | "pulls_url": "https://api.github.com/repos/mietzmithut/Test/pulls{/number}",
60 | "milestones_url": "https://api.github.com/repos/mietzmithut/Test/milestones{/number}",
61 | "notifications_url": "https://api.github.com/repos/mietzmithut/Test/notifications{?since,all,participating}",
62 | "labels_url": "https://api.github.com/repos/mietzmithut/Test/labels{/name}",
63 | "releases_url": "https://api.github.com/repos/mietzmithut/Test/releases{/id}",
64 | "created_at": "2013-06-20T17:05:03Z",
65 | "updated_at": "2014-06-13T21:10:35Z",
66 | "pushed_at": "2013-06-20T20:04:46Z",
67 | "git_url": "git://github.com/mietzmithut/Test.git",
68 | "ssh_url": "git@github.com:mietzmithut/Test.git",
69 | "clone_url": "https://github.com/mietzmithut/Test.git",
70 | "svn_url": "https://github.com/mietzmithut/Test",
71 | "homepage": null,
72 | "size": 132,
73 | "stargazers_count": 0,
74 | "watchers_count": 0,
75 | "language": "Ruby",
76 | "has_issues": true,
77 | "has_downloads": true,
78 | "has_wiki": true,
79 | "has_pages": false,
80 | "forks_count": 0,
81 | "mirror_url": null,
82 | "open_issues_count": 0,
83 | "forks": 0,
84 | "open_issues": 0,
85 | "watchers": 0,
86 | "default_branch": "master",
87 | "network_count": 0,
88 | "subscribers_count": 2
89 | }
--------------------------------------------------------------------------------
/GithubPilotTests/FakeData/user_me.json:
--------------------------------------------------------------------------------
1 | {
2 | "login": "pietbrauer",
3 | "id": 759730,
4 | "avatar_url": "https://avatars.githubusercontent.com/u/759730?v=3",
5 | "gravatar_id": "",
6 | "url": "https://api.github.com/users/pietbrauer",
7 | "html_url": "https://github.com/pietbrauer",
8 | "followers_url": "https://api.github.com/users/pietbrauer/followers",
9 | "following_url": "https://api.github.com/users/pietbrauer/following{/other_user}",
10 | "gists_url": "https://api.github.com/users/pietbrauer/gists{/gist_id}",
11 | "starred_url": "https://api.github.com/users/pietbrauer/starred{/owner}{/repo}",
12 | "subscriptions_url": "https://api.github.com/users/pietbrauer/subscriptions",
13 | "organizations_url": "https://api.github.com/users/pietbrauer/orgs",
14 | "repos_url": "https://api.github.com/users/pietbrauer/repos",
15 | "events_url": "https://api.github.com/users/pietbrauer/events{/privacy}",
16 | "received_events_url": "https://api.github.com/users/pietbrauer/received_events",
17 | "type": "User",
18 | "site_admin": false,
19 | "name": "Piet Brauer",
20 | "company": "XING AG",
21 | "blog": "xing.to/PietBrauer",
22 | "location": "Hamburg",
23 | "email": null,
24 | "hireable": true,
25 | "bio": null,
26 | "public_repos": 6,
27 | "public_gists": 10,
28 | "followers": 41,
29 | "following": 19,
30 | "created_at": "2011-04-29T20:58:36Z",
31 | "updated_at": "2015-01-12T19:42:23Z",
32 | "private_gists": 7,
33 | "total_private_repos": 4,
34 | "owned_private_repos": 4,
35 | "disk_usage": 49064,
36 | "collaborators": 2,
37 | "plan": {
38 | "name": "micro",
39 | "space": 614400,
40 | "collaborators": 0,
41 | "private_repos": 5
42 | }
43 | }
--------------------------------------------------------------------------------
/GithubPilotTests/FakeData/user_mietzmithut.json:
--------------------------------------------------------------------------------
1 | {
2 | "login": "mietzmithut",
3 | "id": 4672699,
4 | "avatar_url": "https://avatars.githubusercontent.com/u/4672699?v=3",
5 | "gravatar_id": "",
6 | "url": "https://api.github.com/users/mietzmithut",
7 | "html_url": "https://github.com/mietzmithut",
8 | "followers_url": "https://api.github.com/users/mietzmithut/followers",
9 | "following_url": "https://api.github.com/users/mietzmithut/following{/other_user}",
10 | "gists_url": "https://api.github.com/users/mietzmithut/gists{/gist_id}",
11 | "starred_url": "https://api.github.com/users/mietzmithut/starred{/owner}{/repo}",
12 | "subscriptions_url": "https://api.github.com/users/mietzmithut/subscriptions",
13 | "organizations_url": "https://api.github.com/users/mietzmithut/orgs",
14 | "repos_url": "https://api.github.com/users/mietzmithut/repos",
15 | "events_url": "https://api.github.com/users/mietzmithut/events{/privacy}",
16 | "received_events_url": "https://api.github.com/users/mietzmithut/received_events",
17 | "type": "User",
18 | "site_admin": false,
19 | "name": "Julia Kallenberg",
20 | "company": "",
21 | "blog": "",
22 | "location": "Hamburg",
23 | "email": "",
24 | "hireable": false,
25 | "bio": null,
26 | "public_repos": 7,
27 | "public_gists": 0,
28 | "followers": 7,
29 | "following": 8,
30 | "created_at": "2013-06-11T18:02:51Z",
31 | "updated_at": "2014-12-22T18:53:41Z"
32 | }
--------------------------------------------------------------------------------
/GithubPilotTests/FakeData/user_repos.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 10824973,
4 | "name": "Test",
5 | "full_name": "mietzmithut/Test",
6 | "owner": {
7 | "login": "mietzmithut",
8 | "id": 4672699,
9 | "avatar_url": "https://avatars.githubusercontent.com/u/4672699?v=3",
10 | "gravatar_id": "",
11 | "url": "https://api.github.com/users/mietzmithut",
12 | "html_url": "https://github.com/mietzmithut",
13 | "followers_url": "https://api.github.com/users/mietzmithut/followers",
14 | "following_url": "https://api.github.com/users/mietzmithut/following{/other_user}",
15 | "gists_url": "https://api.github.com/users/mietzmithut/gists{/gist_id}",
16 | "starred_url": "https://api.github.com/users/mietzmithut/starred{/owner}{/repo}",
17 | "subscriptions_url": "https://api.github.com/users/mietzmithut/subscriptions",
18 | "organizations_url": "https://api.github.com/users/mietzmithut/orgs",
19 | "repos_url": "https://api.github.com/users/mietzmithut/repos",
20 | "events_url": "https://api.github.com/users/mietzmithut/events{/privacy}",
21 | "received_events_url": "https://api.github.com/users/mietzmithut/received_events",
22 | "type": "User",
23 | "site_admin": false
24 | },
25 | "private": false,
26 | "html_url": "https://github.com/mietzmithut/Test",
27 | "description": "",
28 | "fork": false,
29 | "url": "https://api.github.com/repos/mietzmithut/Test",
30 | "forks_url": "https://api.github.com/repos/mietzmithut/Test/forks",
31 | "keys_url": "https://api.github.com/repos/mietzmithut/Test/keys{/key_id}",
32 | "collaborators_url": "https://api.github.com/repos/mietzmithut/Test/collaborators{/collaborator}",
33 | "teams_url": "https://api.github.com/repos/mietzmithut/Test/teams",
34 | "hooks_url": "https://api.github.com/repos/mietzmithut/Test/hooks",
35 | "issue_events_url": "https://api.github.com/repos/mietzmithut/Test/issues/events{/number}",
36 | "events_url": "https://api.github.com/repos/mietzmithut/Test/events",
37 | "assignees_url": "https://api.github.com/repos/mietzmithut/Test/assignees{/user}",
38 | "branches_url": "https://api.github.com/repos/mietzmithut/Test/branches{/branch}",
39 | "tags_url": "https://api.github.com/repos/mietzmithut/Test/tags",
40 | "blobs_url": "https://api.github.com/repos/mietzmithut/Test/git/blobs{/sha}",
41 | "git_tags_url": "https://api.github.com/repos/mietzmithut/Test/git/tags{/sha}",
42 | "git_refs_url": "https://api.github.com/repos/mietzmithut/Test/git/refs{/sha}",
43 | "trees_url": "https://api.github.com/repos/mietzmithut/Test/git/trees{/sha}",
44 | "statuses_url": "https://api.github.com/repos/mietzmithut/Test/statuses/{sha}",
45 | "languages_url": "https://api.github.com/repos/mietzmithut/Test/languages",
46 | "stargazers_url": "https://api.github.com/repos/mietzmithut/Test/stargazers",
47 | "contributors_url": "https://api.github.com/repos/mietzmithut/Test/contributors",
48 | "subscribers_url": "https://api.github.com/repos/mietzmithut/Test/subscribers",
49 | "subscription_url": "https://api.github.com/repos/mietzmithut/Test/subscription",
50 | "commits_url": "https://api.github.com/repos/mietzmithut/Test/commits{/sha}",
51 | "git_commits_url": "https://api.github.com/repos/mietzmithut/Test/git/commits{/sha}",
52 | "comments_url": "https://api.github.com/repos/mietzmithut/Test/comments{/number}",
53 | "issue_comment_url": "https://api.github.com/repos/mietzmithut/Test/issues/comments/{number}",
54 | "contents_url": "https://api.github.com/repos/mietzmithut/Test/contents/{+path}",
55 | "compare_url": "https://api.github.com/repos/mietzmithut/Test/compare/{base}...{head}",
56 | "merges_url": "https://api.github.com/repos/mietzmithut/Test/merges",
57 | "archive_url": "https://api.github.com/repos/mietzmithut/Test/{archive_format}{/ref}",
58 | "downloads_url": "https://api.github.com/repos/mietzmithut/Test/downloads",
59 | "issues_url": "https://api.github.com/repos/mietzmithut/Test/issues{/number}",
60 | "pulls_url": "https://api.github.com/repos/mietzmithut/Test/pulls{/number}",
61 | "milestones_url": "https://api.github.com/repos/mietzmithut/Test/milestones{/number}",
62 | "notifications_url": "https://api.github.com/repos/mietzmithut/Test/notifications{?since,all,participating}",
63 | "labels_url": "https://api.github.com/repos/mietzmithut/Test/labels{/name}",
64 | "releases_url": "https://api.github.com/repos/mietzmithut/Test/releases{/id}",
65 | "created_at": "2013-06-20T17:05:03Z",
66 | "updated_at": "2014-06-13T21:10:35Z",
67 | "pushed_at": "2013-06-20T20:04:46Z",
68 | "git_url": "git://github.com/mietzmithut/Test.git",
69 | "ssh_url": "git@github.com:mietzmithut/Test.git",
70 | "clone_url": "https://github.com/mietzmithut/Test.git",
71 | "svn_url": "https://github.com/mietzmithut/Test",
72 | "homepage": null,
73 | "size": 132,
74 | "stargazers_count": 0,
75 | "watchers_count": 0,
76 | "language": "Ruby",
77 | "has_issues": true,
78 | "has_downloads": true,
79 | "has_wiki": true,
80 | "has_pages": false,
81 | "forks_count": 0,
82 | "mirror_url": null,
83 | "open_issues_count": 0,
84 | "forks": 0,
85 | "open_issues": 0,
86 | "watchers": 0,
87 | "default_branch": "master",
88 | "permissions": {
89 | "admin": false,
90 | "push": true,
91 | "pull": true
92 | }
93 | }
94 | ]
--------------------------------------------------------------------------------
/GithubPilotTests/FakeData/users.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "login": "pietbrauer",
4 | "id": 759730,
5 | "avatar_url": "https://avatars.githubusercontent.com/u/759730?v=3",
6 | "gravatar_id": "",
7 | "url": "https://api.github.com/users/pietbrauer",
8 | "html_url": "https://github.com/pietbrauer",
9 | "followers_url": "https://api.github.com/users/pietbrauer/followers",
10 | "following_url": "https://api.github.com/users/pietbrauer/following{/other_user}",
11 | "gists_url": "https://api.github.com/users/pietbrauer/gists{/gist_id}",
12 | "starred_url": "https://api.github.com/users/pietbrauer/starred{/owner}{/repo}",
13 | "subscriptions_url": "https://api.github.com/users/pietbrauer/subscriptions",
14 | "organizations_url": "https://api.github.com/users/pietbrauer/orgs",
15 | "repos_url": "https://api.github.com/users/pietbrauer/repos",
16 | "events_url": "https://api.github.com/users/pietbrauer/events{/privacy}",
17 | "received_events_url": "https://api.github.com/users/pietbrauer/received_events",
18 | "type": "User",
19 | "site_admin": false,
20 | "name": "Piet Brauer",
21 | "company": "XING AG",
22 | "blog": "xing.to/PietBrauer",
23 | "location": "Hamburg",
24 | "email": null,
25 | "hireable": true,
26 | "bio": null,
27 | "public_repos": 6,
28 | "public_gists": 10,
29 | "followers": 41,
30 | "following": 19,
31 | "created_at": "2011-04-29T20:58:36Z",
32 | "updated_at": "2015-01-12T19:42:23Z",
33 | "private_gists": 7,
34 | "total_private_repos": 4,
35 | "owned_private_repos": 4,
36 | "disk_usage": 49064,
37 | "collaborators": 2,
38 | "plan": {
39 | "name": "micro",
40 | "space": 614400,
41 | "collaborators": 0,
42 | "private_repos": 5
43 | }
44 | }
45 | ]
--------------------------------------------------------------------------------
/GithubPilotTests/GithubPilotTests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
--------------------------------------------------------------------------------
/GithubPilotTests/GithubPilotTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GithubPilotTests.swift
3 | // GithubPilotTests
4 | //
5 | // Created by yansong li on 2016-02-20.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import GithubPilot
11 |
12 | class GithubPilotTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | // Use XCTAssert and related functions to verify your tests produce the correct results.
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | func testZeroBaseUnicodeScalarValue() {
37 | let testString = "9"
38 | let testCharacter = testString.characters[testString.startIndex]
39 | XCTAssert(testCharacter.zeroCharacterBasedunicodeScalarCodePoint() == 9)
40 |
41 | let testStringZero = "0"
42 | let testCharacterZero = testStringZero.characters[testString.startIndex]
43 | XCTAssert(testCharacterZero.zeroCharacterBasedunicodeScalarCodePoint() == 0)
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/GithubPilotTests/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 | NSAppTransportSecurity
14 |
15 | NSAllowsArbitraryLoads
16 |
17 |
18 | CFBundleName
19 | $(PRODUCT_NAME)
20 | CFBundlePackageType
21 | BNDL
22 | CFBundleShortVersionString
23 | 1.0
24 | CFBundleSignature
25 | ????
26 | CFBundleVersion
27 | 1
28 |
29 |
30 |
--------------------------------------------------------------------------------
/GithubPilotTests/StarGazersTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StarGazersTest.swift
3 | // GithubPilot
4 | //
5 | // Created by yansong li on 2016-02-24.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import XCTest
12 | @testable import GithubPilot
13 | import Alamofire
14 |
15 | class StarGazersTests: XCTestCase {
16 | var testClient: GithubNetWorkClient!
17 | var testStarsRoutes: StarsRoutes!
18 |
19 | override func setUp() {
20 | super.setUp()
21 |
22 | let configuration = URLSessionConfiguration.default
23 | configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
24 | let manager = Alamofire.SessionManager(configuration:configuration)
25 | manager.startRequestsImmediately = false
26 | testClient = GithubNetWorkClient(manager: manager,
27 | baseHosts: ["api": "https://api.github.com"])
28 | testStarsRoutes = StarsRoutes(client: testClient)
29 | }
30 |
31 | override func tearDown() {
32 | super.tearDown()
33 | }
34 |
35 | func testWrongParameterStarGazerRequest() {
36 |
37 | let username = "jindulys"
38 |
39 | let expectation = self.expectation(description: "WrongParameter")
40 | testStarsRoutes.getAllStargazersFor(repo: "Hackerlala", owner: username) {
41 | result, error in
42 | if let error = error {
43 | XCTAssertEqual(error, "Bad Request - Code: 404 : Not Found")
44 | expectation.fulfill()
45 | } else {
46 | XCTAssert(false, "Should be error")
47 | expectation.fulfill()
48 | }
49 | }
50 | waitForExpectations(timeout: 5) { (error) in
51 | XCTAssertNil(error, "\(error)")
52 | }
53 | }
54 |
55 | func testStarGazerCountRequest() {
56 |
57 | let username = "jindulys"
58 |
59 | let expectation = self.expectation(description: "StarGazerCount")
60 | testStarsRoutes.getAllStargazersFor(repo: "HackerRankSolutions", owner: username) {
61 | result, error in
62 | if let _ = error {
63 | XCTAssert(false, "Failed Test")
64 | expectation.fulfill()
65 | } else {
66 | if let followers = result {
67 | XCTAssert(followers.count > 100)
68 | }
69 | expectation.fulfill()
70 | }
71 | }
72 | waitForExpectations(timeout: 20) { (error) in
73 | XCTAssertNil(error, "\(error)")
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/GithubPilotTests/TestHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestHelper.swift
3 | // GithubPilot
4 | //
5 | // Created by yansong li on 2016-02-20.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 |
10 | // Get From: https://github.com/nerdishbynature/octokit.swift
11 | import Foundation
12 |
13 | internal class Helper {
14 | internal class func stringFromFile(_ name: String) -> String? {
15 | let bundle = Bundle(for: self)
16 | let path = bundle.path(forResource: name, ofType: "json")
17 | if let path = path {
18 | let string = try? String(contentsOfFile: path, encoding: String.Encoding.utf8)
19 | return string
20 | }
21 | return nil
22 | }
23 |
24 | internal class func JSONDataFromFile(_ name: String) -> Data? {
25 | let bundle = Bundle(for: self)
26 | let path = bundle.path(forResource: name, ofType: "json")!
27 | let data = try? Data(contentsOf: URL(fileURLWithPath: path))
28 | return data
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/GithubPilotTests/UserTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserTests.swift
3 | // GithubPilot
4 | //
5 | // Created by yansong li on 2016-02-20.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import XCTest
12 | @testable import GithubPilot
13 | import Alamofire
14 |
15 | class UserTests: XCTestCase {
16 | var testClient: GithubNetWorkClient!
17 | var testUserRoutes: UsersRoutes!
18 |
19 | override func setUp() {
20 | super.setUp()
21 |
22 | let configuration = URLSessionConfiguration.default
23 | configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
24 | let manager = Alamofire.SessionManager(configuration:configuration)
25 | manager.startRequestsImmediately = false
26 | testClient = GithubNetWorkClient(manager: manager,
27 | baseHosts: ["api": "https://api.github.com"])
28 | testUserRoutes = UsersRoutes(client: testClient)
29 | }
30 |
31 | override func tearDown() {
32 | super.tearDown()
33 | }
34 |
35 | func testReadingUserURLRequest() {
36 |
37 | let username = "jindulys"
38 |
39 | let expectation = self.expectation(description: "\(username)")
40 | testUserRoutes.getUser(username: username).response({ (result, error) -> Void in
41 | if let user = result {
42 | print(user.name)
43 | print(user.htmlURL)
44 | XCTAssertEqual(user.login, username)
45 | expectation.fulfill()
46 | }
47 |
48 | if let rerror = error {
49 | XCTAssert(false, "Error \(rerror.description)")
50 | expectation.fulfill()
51 | }
52 | })
53 |
54 | waitForExpectations(timeout: 5) { (error) in
55 | XCTAssertNil(error, "\(error)")
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 YANSONG LI
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 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '8.0'
3 | # Uncomment this line if you're using Swift
4 | use_frameworks!
5 |
6 | target 'GithubPilot' do
7 | pod 'Alamofire', '~> 4.0'
8 | end
9 |
10 | target 'GithubPilotTests' do
11 | pod 'Nocilla'
12 | end
13 |
14 | post_install do |installer|
15 | installer.pods_project.targets.each do |target|
16 | target.build_configurations.each do |config|
17 | config.build_settings['SWIFT_VERSION'] = '3.0'
18 | config.build_settings['ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES'] = 'YES'
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (4.0.1)
3 | - Nocilla (0.11.0)
4 |
5 | DEPENDENCIES:
6 | - Alamofire (~> 4.0)
7 | - Nocilla
8 |
9 | SPEC CHECKSUMS:
10 | Alamofire: 7682d43245de14874acd142ec137b144aa1dd335
11 | Nocilla: 7af7a386071150cc8aa5da4da97d060f049dd61c
12 |
13 | PODFILE CHECKSUM: 1151873ca50cd5df9c5a22014903d6e8551e4145
14 |
15 | COCOAPODS: 1.1.0.rc.2
16 |
--------------------------------------------------------------------------------
/Pods/Alamofire/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DispatchQueue+Alamofire.swift
3 | //
4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Dispatch
26 | import Foundation
27 |
28 | extension DispatchQueue {
29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
33 |
34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
35 | asyncAfter(deadline: .now() + delay, execute: closure)
36 | }
37 |
38 | func syncResult(_ closure: () -> T) -> T {
39 | var result: T!
40 | sync { result = closure() }
41 | return result
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Pods/Alamofire/Source/Notifications.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Notifications.swift
3 | //
4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | extension Notification.Name {
28 | /// Used as a namespace for all `URLSessionTask` related notifications.
29 | public struct Task {
30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`.
31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume")
32 |
33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`.
34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend")
35 |
36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`.
37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel")
38 |
39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`.
40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete")
41 | }
42 | }
43 |
44 | // MARK: -
45 |
46 | extension Notification {
47 | /// Used as a namespace for all `Notification` user info dictionary keys.
48 | public struct Key {
49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification.
50 | public static let Task = "org.alamofire.notification.key.task"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Pods/Alamofire/Source/Result.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Result.swift
3 | //
4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | /// Used to represent whether a request was successful or encountered an error.
28 | ///
29 | /// - success: The request and all post processing operations were successful resulting in the serialization of the
30 | /// provided associated value.
31 | ///
32 | /// - failure: The request encountered an error resulting in a failure. The associated values are the original data
33 | /// provided by the server as well as the error that caused the failure.
34 | public enum Result {
35 | case success(Value)
36 | case failure(Error)
37 |
38 | /// Returns `true` if the result is a success, `false` otherwise.
39 | public var isSuccess: Bool {
40 | switch self {
41 | case .success:
42 | return true
43 | case .failure:
44 | return false
45 | }
46 | }
47 |
48 | /// Returns `true` if the result is a failure, `false` otherwise.
49 | public var isFailure: Bool {
50 | return !isSuccess
51 | }
52 |
53 | /// Returns the associated value if the result is a success, `nil` otherwise.
54 | public var value: Value? {
55 | switch self {
56 | case .success(let value):
57 | return value
58 | case .failure:
59 | return nil
60 | }
61 | }
62 |
63 | /// Returns the associated error value if the result is a failure, `nil` otherwise.
64 | public var error: Error? {
65 | switch self {
66 | case .success:
67 | return nil
68 | case .failure(let error):
69 | return error
70 | }
71 | }
72 | }
73 |
74 | // MARK: - CustomStringConvertible
75 |
76 | extension Result: CustomStringConvertible {
77 | /// The textual representation used when written to an output stream, which includes whether the result was a
78 | /// success or failure.
79 | public var description: String {
80 | switch self {
81 | case .success:
82 | return "SUCCESS"
83 | case .failure:
84 | return "FAILURE"
85 | }
86 | }
87 | }
88 |
89 | // MARK: - CustomDebugStringConvertible
90 |
91 | extension Result: CustomDebugStringConvertible {
92 | /// The debug textual representation used when written to an output stream, which includes whether the result was a
93 | /// success or failure in addition to the value or error.
94 | public var debugDescription: String {
95 | switch self {
96 | case .success(let value):
97 | return "SUCCESS: \(value)"
98 | case .failure(let error):
99 | return "FAILURE: \(error)"
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Pods/Alamofire/Source/Timeline.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Timeline.swift
3 | //
4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | /// Responsible for computing the timing metrics for the complete lifecycle of a `Request`.
28 | public struct Timeline {
29 | /// The time the request was initialized.
30 | public let requestStartTime: CFAbsoluteTime
31 |
32 | /// The time the first bytes were received from or sent to the server.
33 | public let initialResponseTime: CFAbsoluteTime
34 |
35 | /// The time when the request was completed.
36 | public let requestCompletedTime: CFAbsoluteTime
37 |
38 | /// The time when the response serialization was completed.
39 | public let serializationCompletedTime: CFAbsoluteTime
40 |
41 | /// The time interval in seconds from the time the request started to the initial response from the server.
42 | public let latency: TimeInterval
43 |
44 | /// The time interval in seconds from the time the request started to the time the request completed.
45 | public let requestDuration: TimeInterval
46 |
47 | /// The time interval in seconds from the time the request completed to the time response serialization completed.
48 | public let serializationDuration: TimeInterval
49 |
50 | /// The time interval in seconds from the time the request started to the time response serialization completed.
51 | public let totalDuration: TimeInterval
52 |
53 | /// Creates a new `Timeline` instance with the specified request times.
54 | ///
55 | /// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`.
56 | /// - parameter initialResponseTime: The time the first bytes were received from or sent to the server.
57 | /// Defaults to `0.0`.
58 | /// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`.
59 | /// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
60 | /// to `0.0`.
61 | ///
62 | /// - returns: The new `Timeline` instance.
63 | public init(
64 | requestStartTime: CFAbsoluteTime = 0.0,
65 | initialResponseTime: CFAbsoluteTime = 0.0,
66 | requestCompletedTime: CFAbsoluteTime = 0.0,
67 | serializationCompletedTime: CFAbsoluteTime = 0.0)
68 | {
69 | self.requestStartTime = requestStartTime
70 | self.initialResponseTime = initialResponseTime
71 | self.requestCompletedTime = requestCompletedTime
72 | self.serializationCompletedTime = serializationCompletedTime
73 |
74 | self.latency = initialResponseTime - requestStartTime
75 | self.requestDuration = requestCompletedTime - requestStartTime
76 | self.serializationDuration = serializationCompletedTime - requestCompletedTime
77 | self.totalDuration = serializationCompletedTime - requestStartTime
78 | }
79 | }
80 |
81 | // MARK: - CustomStringConvertible
82 |
83 | extension Timeline: CustomStringConvertible {
84 | /// The textual representation used when written to an output stream, which includes the latency, the request
85 | /// duration and the total duration.
86 | public var description: String {
87 | let latency = String(format: "%.3f", self.latency)
88 | let requestDuration = String(format: "%.3f", self.requestDuration)
89 | let serializationDuration = String(format: "%.3f", self.serializationDuration)
90 | let totalDuration = String(format: "%.3f", self.totalDuration)
91 |
92 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
93 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
94 | let timings = [
95 | "\"Latency\": " + latency + " secs",
96 | "\"Request Duration\": " + requestDuration + " secs",
97 | "\"Serialization Duration\": " + serializationDuration + " secs",
98 | "\"Total Duration\": " + totalDuration + " secs"
99 | ]
100 |
101 | return "Timeline: { " + timings.joined(separator: ", ") + " }"
102 | }
103 | }
104 |
105 | // MARK: - CustomDebugStringConvertible
106 |
107 | extension Timeline: CustomDebugStringConvertible {
108 | /// The textual representation used when written to an output stream, which includes the request start time, the
109 | /// initial response time, the request completed time, the serialization completed time, the latency, the request
110 | /// duration and the total duration.
111 | public var debugDescription: String {
112 | let requestStartTime = String(format: "%.3f", self.requestStartTime)
113 | let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
114 | let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
115 | let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
116 | let latency = String(format: "%.3f", self.latency)
117 | let requestDuration = String(format: "%.3f", self.requestDuration)
118 | let serializationDuration = String(format: "%.3f", self.serializationDuration)
119 | let totalDuration = String(format: "%.3f", self.totalDuration)
120 |
121 | // NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
122 | // fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
123 | let timings = [
124 | "\"Request Start Time\": " + requestStartTime,
125 | "\"Initial Response Time\": " + initialResponseTime,
126 | "\"Request Completed Time\": " + requestCompletedTime,
127 | "\"Serialization Completed Time\": " + serializationCompletedTime,
128 | "\"Latency\": " + latency + " secs",
129 | "\"Request Duration\": " + requestDuration + " secs",
130 | "\"Serialization Duration\": " + serializationDuration + " secs",
131 | "\"Total Duration\": " + totalDuration + " secs"
132 | ]
133 |
134 | return "Timeline: { " + timings.joined(separator: ", ") + " }"
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Pods/Manifest.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Alamofire (4.0.1)
3 | - Nocilla (0.11.0)
4 |
5 | DEPENDENCIES:
6 | - Alamofire (~> 4.0)
7 | - Nocilla
8 |
9 | SPEC CHECKSUMS:
10 | Alamofire: 7682d43245de14874acd142ec137b144aa1dd335
11 | Nocilla: 7af7a386071150cc8aa5da4da97d060f049dd61c
12 |
13 | PODFILE CHECKSUM: 1151873ca50cd5df9c5a22014903d6e8551e4145
14 |
15 | COCOAPODS: 1.1.0.rc.2
16 |
--------------------------------------------------------------------------------
/Pods/Nocilla/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012 Luis Solano Bonet
2 | MIT License
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Categories/NSData+Nocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPBody.h"
3 |
4 | @interface NSData (Nocilla)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Categories/NSData+Nocilla.m:
--------------------------------------------------------------------------------
1 | #import "NSData+Nocilla.h"
2 |
3 | @implementation NSData (Nocilla)
4 |
5 | - (NSData *)data {
6 | return self;
7 | }
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Categories/NSString+Nocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPBody.h"
3 |
4 | @interface NSString (Nocilla)
5 |
6 | - (NSRegularExpression *)regex;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Categories/NSString+Nocilla.m:
--------------------------------------------------------------------------------
1 | #import "NSString+Nocilla.h"
2 |
3 | @implementation NSString (Nocilla)
4 |
5 | - (NSRegularExpression *)regex {
6 | NSError *error = nil;
7 | NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:self options:0 error:&error];
8 | if (error) {
9 | [NSException raise:NSInvalidArgumentException format:@"Invalid regex pattern: %@\nError: %@", self, error];
10 | }
11 | return regex;
12 | }
13 |
14 | - (NSData *)data {
15 | return [self dataUsingEncoding:NSUTF8StringEncoding];
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSHTTPRequestDSLRepresentation.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface LSHTTPRequestDSLRepresentation : NSObject
5 | - (id)initWithRequest:(id)request;
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSHTTPRequestDSLRepresentation.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPRequestDSLRepresentation.h"
2 |
3 | @interface LSHTTPRequestDSLRepresentation ()
4 | @property (nonatomic, strong) id request;
5 | @end
6 |
7 | @implementation LSHTTPRequestDSLRepresentation
8 | - (id)initWithRequest:(id)request {
9 | self = [super init];
10 | if (self) {
11 | _request = request;
12 | }
13 | return self;
14 | }
15 |
16 | - (NSString *)description {
17 | NSMutableString *result = [NSMutableString stringWithFormat:@"stubRequest(@\"%@\", @\"%@\")", self.request.method, [self.request.url absoluteString]];
18 | if (self.request.headers.count) {
19 | [result appendString:@".\nwithHeaders(@{ "];
20 | NSMutableArray *headerElements = [NSMutableArray arrayWithCapacity:self.request.headers.count];
21 |
22 | NSArray *descriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]];
23 | NSArray * sortedHeaders = [[self.request.headers allKeys] sortedArrayUsingDescriptors:descriptors];
24 |
25 | for (NSString * header in sortedHeaders) {
26 | NSString *value = [self.request.headers objectForKey:header];
27 | [headerElements addObject:[NSString stringWithFormat:@"@\"%@\": @\"%@\"", header, value]];
28 | }
29 | [result appendString:[headerElements componentsJoinedByString:@", "]];
30 | [result appendString:@" })"];
31 | }
32 | if (self.request.body.length) {
33 | NSString *escapedBody = [[NSString alloc] initWithData:self.request.body encoding:NSUTF8StringEncoding];
34 | escapedBody = [escapedBody stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
35 | [result appendFormat:@".\nwithBody(@\"%@\")", escapedBody];
36 | }
37 | return [NSString stringWithFormat:@"%@;", result];
38 | }
39 | @end
40 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSStubRequestDSL.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "NSString+Matcheable.h"
3 | #import "NSRegularExpression+Matcheable.h"
4 | #import "NSData+Matcheable.h"
5 |
6 | @class LSStubRequestDSL;
7 | @class LSStubResponseDSL;
8 | @class LSStubRequest;
9 |
10 | @protocol LSHTTPBody;
11 |
12 | typedef LSStubRequestDSL *(^WithHeaderMethod)(NSString *, NSString *);
13 | typedef LSStubRequestDSL *(^WithHeadersMethod)(NSDictionary *);
14 | typedef LSStubRequestDSL *(^AndBodyMethod)(id);
15 | typedef LSStubResponseDSL *(^AndReturnMethod)(NSInteger);
16 | typedef LSStubResponseDSL *(^AndReturnRawResponseMethod)(NSData *rawResponseData);
17 | typedef void (^AndFailWithErrorMethod)(NSError *error);
18 |
19 | @interface LSStubRequestDSL : NSObject
20 | - (id)initWithRequest:(LSStubRequest *)request;
21 |
22 | @property (nonatomic, strong, readonly) WithHeaderMethod withHeader;
23 | @property (nonatomic, strong, readonly) WithHeadersMethod withHeaders;
24 | @property (nonatomic, strong, readonly) AndBodyMethod withBody;
25 | @property (nonatomic, strong, readonly) AndReturnMethod andReturn;
26 | @property (nonatomic, strong, readonly) AndReturnRawResponseMethod andReturnRawResponse;
27 | @property (nonatomic, strong, readonly) AndFailWithErrorMethod andFailWithError;
28 |
29 | @end
30 |
31 | #ifdef __cplusplus
32 | extern "C" {
33 | #endif
34 |
35 | LSStubRequestDSL * stubRequest(NSString *method, id url);
36 |
37 | #ifdef __cplusplus
38 | }
39 | #endif
40 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSStubRequestDSL.m:
--------------------------------------------------------------------------------
1 | #import "LSStubRequestDSL.h"
2 | #import "LSStubResponseDSL.h"
3 | #import "LSStubRequest.h"
4 | #import "LSNocilla.h"
5 |
6 | @interface LSStubRequestDSL ()
7 | @property (nonatomic, strong) LSStubRequest *request;
8 | @end
9 |
10 | @implementation LSStubRequestDSL
11 |
12 | - (id)initWithRequest:(LSStubRequest *)request {
13 | self = [super init];
14 | if (self) {
15 | _request = request;
16 | }
17 | return self;
18 | }
19 | - (WithHeadersMethod)withHeaders {
20 | return ^(NSDictionary *headers) {
21 | for (NSString *header in headers) {
22 | NSString *value = [headers objectForKey:header];
23 | [self.request setHeader:header value:value];
24 | }
25 | return self;
26 | };
27 | }
28 |
29 | - (WithHeaderMethod)withHeader {
30 | return ^(NSString * header, NSString * value) {
31 | [self.request setHeader:header value:value];
32 | return self;
33 | };
34 | }
35 |
36 | - (AndBodyMethod)withBody {
37 | return ^(id body) {
38 | self.request.body = body.matcher;
39 | return self;
40 | };
41 | }
42 |
43 | - (AndReturnMethod)andReturn {
44 | return ^(NSInteger statusCode) {
45 | self.request.response = [[LSStubResponse alloc] initWithStatusCode:statusCode];
46 | LSStubResponseDSL *responseDSL = [[LSStubResponseDSL alloc] initWithResponse:self.request.response];
47 | return responseDSL;
48 | };
49 | }
50 |
51 | - (AndReturnRawResponseMethod)andReturnRawResponse {
52 | return ^(NSData *rawResponseData) {
53 | self.request.response = [[LSStubResponse alloc] initWithRawResponse:rawResponseData];
54 | LSStubResponseDSL *responseDSL = [[LSStubResponseDSL alloc] initWithResponse:self.request.response];
55 | return responseDSL;
56 | };
57 | }
58 |
59 | - (AndFailWithErrorMethod)andFailWithError {
60 | return ^(NSError *error) {
61 | self.request.response = [[LSStubResponse alloc] initWithError:error];
62 | };
63 | }
64 |
65 | @end
66 |
67 | LSStubRequestDSL * stubRequest(NSString *method, id url) {
68 | LSStubRequest *request = [[LSStubRequest alloc] initWithMethod:method urlMatcher:url.matcher];
69 | LSStubRequestDSL *dsl = [[LSStubRequestDSL alloc] initWithRequest:request];
70 | [[LSNocilla sharedInstance] addStubbedRequest:request];
71 | return dsl;
72 | }
73 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSStubResponseDSL.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class LSStubResponse;
4 | @class LSStubResponseDSL;
5 |
6 | @protocol LSHTTPBody;
7 |
8 | typedef LSStubResponseDSL *(^ResponseWithBodyMethod)(id);
9 | typedef LSStubResponseDSL *(^ResponseWithHeaderMethod)(NSString *, NSString *);
10 | typedef LSStubResponseDSL *(^ResponseWithHeadersMethod)(NSDictionary *);
11 |
12 | @interface LSStubResponseDSL : NSObject
13 | - (id)initWithResponse:(LSStubResponse *)response;
14 |
15 | @property (nonatomic, strong, readonly) ResponseWithHeaderMethod withHeader;
16 | @property (nonatomic, strong, readonly) ResponseWithHeadersMethod withHeaders;
17 | @property (nonatomic, strong, readonly) ResponseWithBodyMethod withBody;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/DSL/LSStubResponseDSL.m:
--------------------------------------------------------------------------------
1 | #import "LSStubResponseDSL.h"
2 | #import "LSStubResponse.h"
3 | #import "LSHTTPBody.h"
4 |
5 | @interface LSStubResponseDSL ()
6 | @property (nonatomic, strong) LSStubResponse *response;
7 | @end
8 |
9 | @implementation LSStubResponseDSL
10 | - (id)initWithResponse:(LSStubResponse *)response {
11 | self = [super init];
12 | if (self) {
13 | _response = response;
14 | }
15 | return self;
16 | }
17 | - (ResponseWithHeaderMethod)withHeader {
18 | return ^(NSString * header, NSString * value) {
19 | [self.response setHeader:header value:value];
20 | return self;
21 | };
22 | }
23 |
24 | - (ResponseWithHeadersMethod)withHeaders; {
25 | return ^(NSDictionary *headers) {
26 | for (NSString *header in headers) {
27 | NSString *value = [headers objectForKey:header];
28 | [self.response setHeader:header value:value];
29 | }
30 | return self;
31 | };
32 | }
33 |
34 | - (ResponseWithBodyMethod)withBody {
35 | return ^(id body) {
36 | self.response.body = [body data];
37 | return self;
38 | };
39 | }
40 | @end
41 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Diff/LSHTTPRequestDiff.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface LSHTTPRequestDiff : NSObject
5 | @property (nonatomic, assign, readonly, getter = isEmpty) BOOL empty;
6 |
7 | - (id)initWithRequest:(id)oneRequest andRequest:(id)anotherRequest;
8 | @end
9 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Diff/LSHTTPRequestDiff.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPRequestDiff.h"
2 |
3 | @interface LSHTTPRequestDiff ()
4 | @property (nonatomic, strong) idoneRequest;
5 | @property (nonatomic, strong) idanotherRequest;
6 |
7 | - (BOOL)isMethodDifferent;
8 | - (BOOL)isUrlDifferent;
9 | - (BOOL)areHeadersDifferent;
10 | - (BOOL)isBodyDifferent;
11 |
12 | - (void)appendMethodDiff:(NSMutableString *)diff;
13 | - (void)appendUrlDiff:(NSMutableString *)diff;
14 | - (void)appendHeadersDiff:(NSMutableString *)diff;
15 | - (void)appendBodyDiff:(NSMutableString *)diff;
16 | @end
17 |
18 | @implementation LSHTTPRequestDiff
19 | - (id)initWithRequest:(id)oneRequest andRequest:(id)anotherRequest {
20 | self = [super init];
21 | if (self) {
22 | _oneRequest = oneRequest;
23 | _anotherRequest = anotherRequest;
24 | }
25 | return self;
26 | }
27 |
28 | - (BOOL)isEmpty {
29 | if ([self isMethodDifferent] ||
30 | [self isUrlDifferent] ||
31 | [self areHeadersDifferent] ||
32 | [self isBodyDifferent]) {
33 | return NO;
34 | }
35 | return YES;
36 | }
37 |
38 | - (NSString *)description {
39 | NSMutableString *diff = [@"" mutableCopy];
40 | if ([self isMethodDifferent]) {
41 | [self appendMethodDiff:diff];
42 | }
43 | if ([self isUrlDifferent]) {
44 | [self appendUrlDiff:diff];
45 | }
46 | if([self areHeadersDifferent]) {
47 | [self appendHeadersDiff:diff];
48 | }
49 | if([self isBodyDifferent]) {
50 | [self appendBodyDiff:diff];
51 | }
52 | return [NSString stringWithString:diff];
53 | }
54 |
55 | #pragma mark - Private Methods
56 | - (BOOL)isMethodDifferent {
57 | return ![self.oneRequest.method isEqualToString:self.anotherRequest.method];
58 | }
59 |
60 | - (BOOL)isUrlDifferent {
61 | return ![self.oneRequest.url isEqual:self.anotherRequest.url];
62 | }
63 |
64 | - (BOOL)areHeadersDifferent {
65 | return ![self.oneRequest.headers isEqual:self.anotherRequest.headers];
66 | }
67 |
68 | - (BOOL)isBodyDifferent {
69 | return (((self.oneRequest.body) && (![self.oneRequest.body isEqual:self.anotherRequest.body])) ||
70 | ((self.anotherRequest.body) && (![self.anotherRequest.body isEqual:self.oneRequest.body])));
71 | }
72 |
73 | - (void)appendMethodDiff:(NSMutableString *)diff {
74 | [diff appendFormat:@"- Method: %@\n+ Method: %@\n", self.oneRequest.method, self.anotherRequest.method];
75 | }
76 |
77 | - (void)appendUrlDiff:(NSMutableString *)diff {
78 | [diff appendFormat:@"- URL: %@\n+ URL: %@\n", [self.oneRequest.url absoluteString], [self.anotherRequest.url absoluteString]];
79 | }
80 |
81 | - (void)appendHeadersDiff:(NSMutableString *)diff {
82 | [diff appendString:@" Headers:\n"];
83 | NSSet *headersInOneButNotInTheOther = [self.oneRequest.headers keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
84 | return ![self.anotherRequest.headers objectForKey:key] || ![obj isEqual:[self.anotherRequest.headers objectForKey:key]];
85 | }];
86 | NSSet *headersInTheOtherButNotInOne = [self.anotherRequest.headers keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
87 | return ![self.oneRequest.headers objectForKey:key] || ![obj isEqual:[self.oneRequest.headers objectForKey:key]];
88 | }];
89 |
90 | NSArray *descriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"" ascending:YES]];
91 | NSArray * sortedHeadersInOneButNotInTheOther = [headersInOneButNotInTheOther sortedArrayUsingDescriptors:descriptors];
92 | NSArray * sortedHeadersInTheOtherButNotInOne = [headersInTheOtherButNotInOne sortedArrayUsingDescriptors:descriptors];
93 | for (NSString *header in sortedHeadersInOneButNotInTheOther) {
94 | NSString *value = [self.oneRequest.headers objectForKey:header];
95 | [diff appendFormat:@"-\t\"%@\": \"%@\"\n", header, value];
96 |
97 | }
98 | for (NSString *header in sortedHeadersInTheOtherButNotInOne) {
99 | NSString *value = [self.anotherRequest.headers objectForKey:header];
100 | [diff appendFormat:@"+\t\"%@\": \"%@\"\n", header, value];
101 | }
102 | }
103 |
104 | - (void)appendBodyDiff:(NSMutableString *)diff {
105 | NSString *oneBody = [[NSString alloc] initWithData:self.oneRequest.body encoding:NSUTF8StringEncoding];
106 | if (oneBody.length) {
107 | [diff appendFormat:@"- Body: \"%@\"\n", oneBody];
108 | }
109 | NSString *anotherBody = [[NSString alloc] initWithData:self.anotherRequest.body encoding:NSUTF8StringEncoding];
110 | if (anotherBody.length) {
111 | [diff appendFormat:@"+ Body: \"%@\"\n", anotherBody];
112 | }
113 | }
114 | @end
115 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/ASIHTTPRequestStub.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface ASIHTTPRequestStub : NSObject
4 | - (int)stub_responseStatusCode;
5 | - (NSData *)stub_responseData;
6 | - (NSDictionary *)stub_responseHeaders;
7 | - (void)stub_startRequest;
8 | @end
9 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/ASIHTTPRequestStub.m:
--------------------------------------------------------------------------------
1 | #import "ASIHTTPRequestStub.h"
2 | #import "LSStubResponse.h"
3 | #import "LSNocilla.h"
4 | #import "LSASIHTTPRequestAdapter.h"
5 | #import
6 |
7 | @interface ASIHTTPRequestStub ()
8 | @property (nonatomic, strong) LSStubResponse *stubResponse;
9 | @end
10 |
11 | @interface ASIHTTPRequestStub (Private)
12 | - (void)failWithError:(NSError *)error;
13 | - (void)requestFinished;
14 | - (void)markAsFinished;
15 | @end
16 |
17 | static void const * ASIHTTPRequestStubResponseKey = &ASIHTTPRequestStubResponseKey;
18 |
19 | @implementation ASIHTTPRequestStub
20 |
21 | - (void)setStubResponse:(LSStubResponse *)stubResponse {
22 | objc_setAssociatedObject(self, ASIHTTPRequestStubResponseKey, stubResponse, OBJC_ASSOCIATION_RETAIN);
23 | }
24 |
25 | - (LSStubResponse *)stubResponse {
26 | return objc_getAssociatedObject(self, ASIHTTPRequestStubResponseKey);
27 | }
28 |
29 | - (int)stub_responseStatusCode {
30 | return (int)self.stubResponse.statusCode;
31 | }
32 |
33 | - (NSData *)stub_responseData {
34 | return self.stubResponse.body;
35 | }
36 |
37 | - (NSDictionary *)stub_responseHeaders {
38 | return self.stubResponse.headers;
39 | }
40 |
41 | - (void)stub_startRequest {
42 | self.stubResponse = [[LSNocilla sharedInstance] responseForRequest:[[LSASIHTTPRequestAdapter alloc] initWithASIHTTPRequest:(id)self]];
43 |
44 | if (self.stubResponse.shouldFail) {
45 | [self failWithError:self.stubResponse.error];
46 | } else {
47 | [self requestFinished];
48 | }
49 | [self markAsFinished];
50 | }
51 |
52 | @end
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestAdapter.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @class ASIHTTPRequest;
5 |
6 | @interface LSASIHTTPRequestAdapter : NSObject
7 |
8 | - (instancetype)initWithASIHTTPRequest:(ASIHTTPRequest *)request;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestAdapter.m:
--------------------------------------------------------------------------------
1 | #import "LSASIHTTPRequestAdapter.h"
2 |
3 | @interface ASIHTTPRequest
4 |
5 | @property (nonatomic, strong, readonly) NSURL *url;
6 | @property (nonatomic, strong, readonly) NSString *requestMethod;
7 | @property (nonatomic, strong, readonly) NSDictionary *requestHeaders;
8 | @property (nonatomic, strong, readonly) NSData *postBody;
9 |
10 | @end
11 |
12 | @interface LSASIHTTPRequestAdapter ()
13 | @property (nonatomic, strong) ASIHTTPRequest *request;
14 | @end
15 |
16 | @implementation LSASIHTTPRequestAdapter
17 |
18 | - (instancetype)initWithASIHTTPRequest:(ASIHTTPRequest *)request {
19 | self = [super init];
20 | if (self) {
21 | _request = request;
22 | }
23 | return self;
24 | }
25 |
26 | - (NSURL *)url {
27 | return self.request.url;
28 | }
29 |
30 | - (NSString *)method {
31 | return self.request.requestMethod;
32 | }
33 |
34 | - (NSDictionary *)headers {
35 | return self.request.requestHeaders;
36 | }
37 |
38 | - (NSData *)body {
39 | return self.request.postBody;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestHook.h:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @interface LSASIHTTPRequestHook : LSHTTPClientHook
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/ASIHTTPRequest/LSASIHTTPRequestHook.m:
--------------------------------------------------------------------------------
1 | #import "LSASIHTTPRequestHook.h"
2 | #import "ASIHTTPRequestStub.h"
3 | #import
4 |
5 | @implementation LSASIHTTPRequestHook
6 |
7 | - (void)load {
8 | if (!NSClassFromString(@"ASIHTTPRequest")) return;
9 | [self swizzleASIHTTPRequest];
10 | }
11 |
12 | - (void)unload {
13 | if (!NSClassFromString(@"ASIHTTPRequest")) return;
14 | [self swizzleASIHTTPRequest];
15 | }
16 |
17 | #pragma mark - Internal Methods
18 |
19 | - (void)swizzleASIHTTPRequest {
20 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseStatusCode") withSelector:@selector(stub_responseStatusCode)];
21 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseData") withSelector:@selector(stub_responseData)];
22 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"responseHeaders") withSelector:@selector(stub_responseHeaders)];
23 | [self swizzleASIHTTPSelector:NSSelectorFromString(@"startRequest") withSelector:@selector(stub_startRequest)];
24 | [self addMethodToASIHTTPRequest:NSSelectorFromString(@"stubResponse")];
25 | [self addMethodToASIHTTPRequest:NSSelectorFromString(@"setStubResponse:")];
26 | }
27 |
28 | - (void)swizzleASIHTTPSelector:(SEL)original withSelector:(SEL)stub {
29 | Class asiHttpRequest = NSClassFromString(@"ASIHTTPRequest");
30 | Method originalMethod = class_getInstanceMethod(asiHttpRequest, original);
31 | Method stubMethod = class_getInstanceMethod([ASIHTTPRequestStub class], stub);
32 | if (!originalMethod || !stubMethod) {
33 | [self fail];
34 | }
35 | method_exchangeImplementations(originalMethod, stubMethod);
36 | }
37 |
38 | - (void)addMethodToASIHTTPRequest:(SEL)newMethod {
39 | Method method = class_getInstanceMethod([ASIHTTPRequestStub class], newMethod);
40 | const char *types = method_getTypeEncoding(method);
41 | class_addMethod(NSClassFromString(@"ASIHTTPRequest"), newMethod, class_getMethodImplementation([ASIHTTPRequestStub class], newMethod), types);
42 | }
43 |
44 | - (void)fail {
45 | [NSException raise:NSInternalInconsistencyException format:@"Couldn't load ASIHTTPRequest hook."];
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/LSHTTPClientHook.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSHTTPClientHook : NSObject
4 | - (void)load;
5 | - (void)unload;
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/LSHTTPClientHook.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @implementation LSHTTPClientHook
4 | - (void)load {
5 | [NSException raise:NSInternalInconsistencyException
6 | format:@"Method '%@' not implemented. Subclass '%@' and override it", NSStringFromSelector(_cmd), NSStringFromClass([self class])];
7 | }
8 |
9 | - (void)unload {
10 | [NSException raise:NSInternalInconsistencyException
11 | format:@"Method '%@' not implemented. Subclass '%@' and override it", NSStringFromSelector(_cmd), NSStringFromClass([self class])];
12 | }
13 | @end
14 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSHTTPStubURLProtocol : NSURLProtocol
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/LSHTTPStubURLProtocol.m:
--------------------------------------------------------------------------------
1 | #import "LSHTTPStubURLProtocol.h"
2 | #import "LSNocilla.h"
3 | #import "NSURLRequest+LSHTTPRequest.h"
4 | #import "LSStubRequest.h"
5 | #import "NSURLRequest+DSL.h"
6 |
7 | @implementation LSHTTPStubURLProtocol
8 |
9 | + (BOOL)canInitWithRequest:(NSURLRequest *)request {
10 | return [@[ @"http", @"https" ] containsObject:request.URL.scheme];
11 | }
12 |
13 | + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
14 | return request;
15 | }
16 | + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
17 | return NO;
18 | }
19 |
20 | - (void)startLoading {
21 | NSURLRequest* request = [self request];
22 | id client = [self client];
23 |
24 | LSStubResponse* stubbedResponse = [[LSNocilla sharedInstance] responseForRequest:request];
25 |
26 | NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
27 | [cookieStorage setCookies:[NSHTTPCookie cookiesWithResponseHeaderFields:stubbedResponse.headers forURL:request.url]
28 | forURL:request.URL mainDocumentURL:request.URL];
29 |
30 | if (stubbedResponse.shouldFail) {
31 | [client URLProtocol:self didFailWithError:stubbedResponse.error];
32 | } else {
33 | NSHTTPURLResponse* urlResponse = [[NSHTTPURLResponse alloc] initWithURL:request.URL
34 | statusCode:stubbedResponse.statusCode
35 | HTTPVersion:nil
36 | headerFields:stubbedResponse.headers];
37 |
38 | if (stubbedResponse.statusCode < 300 || stubbedResponse.statusCode > 399
39 | || stubbedResponse.statusCode == 304 || stubbedResponse.statusCode == 305 ) {
40 | NSData *body = stubbedResponse.body;
41 |
42 | [client URLProtocol:self didReceiveResponse:urlResponse
43 | cacheStoragePolicy:NSURLCacheStorageNotAllowed];
44 | [client URLProtocol:self didLoadData:body];
45 | [client URLProtocolDidFinishLoading:self];
46 | } else {
47 |
48 | NSURL *newURL = [NSURL URLWithString:[stubbedResponse.headers objectForKey:@"Location"] relativeToURL:request.URL];
49 | NSMutableURLRequest *redirectRequest = [NSMutableURLRequest requestWithURL:newURL];
50 |
51 | [redirectRequest setAllHTTPHeaderFields:[NSHTTPCookie requestHeaderFieldsWithCookies:[cookieStorage cookiesForURL:newURL]]];
52 |
53 | [client URLProtocol:self
54 | wasRedirectedToRequest:redirectRequest
55 | redirectResponse:urlResponse];
56 | // According to: https://developer.apple.com/library/ios/samplecode/CustomHTTPProtocol/Listings/CustomHTTPProtocol_Core_Code_CustomHTTPProtocol_m.html
57 | // needs to abort the original request
58 | [client URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
59 |
60 | }
61 | }
62 | }
63 |
64 | - (void)stopLoading {
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/LSNSURLHook.h:
--------------------------------------------------------------------------------
1 | #import "LSHTTPClientHook.h"
2 |
3 | @interface LSNSURLHook : LSHTTPClientHook
4 |
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/LSNSURLHook.m:
--------------------------------------------------------------------------------
1 | #import "LSNSURLHook.h"
2 | #import "LSHTTPStubURLProtocol.h"
3 |
4 | @implementation LSNSURLHook
5 |
6 | - (void)load {
7 | [NSURLProtocol registerClass:[LSHTTPStubURLProtocol class]];
8 | }
9 |
10 | - (void)unload {
11 | [NSURLProtocol unregisterClass:[LSHTTPStubURLProtocol class]];
12 | }
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/NSURLRequest+DSL.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface NSURLRequest (DSL)
4 | - (NSString *)toNocillaDSL;
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/NSURLRequest+DSL.m:
--------------------------------------------------------------------------------
1 | #import "NSURLRequest+DSL.h"
2 | #import "LSHTTPRequestDSLRepresentation.h"
3 | #import "NSURLRequest+LSHTTPRequest.h"
4 |
5 | @implementation NSURLRequest (DSL)
6 | - (NSString *)toNocillaDSL {
7 | return [[[LSHTTPRequestDSLRepresentation alloc] initWithRequest:self] description];
8 | }
9 | @end
10 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/NSURLRequest+LSHTTPRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPRequest.h"
3 |
4 | @interface NSURLRequest (LSHTTPRequest)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLRequest/NSURLRequest+LSHTTPRequest.m:
--------------------------------------------------------------------------------
1 | #import "NSURLRequest+LSHTTPRequest.h"
2 |
3 | @implementation NSURLRequest (LSHTTPRequest)
4 |
5 | - (NSURL*)url {
6 | return self.URL;
7 | }
8 |
9 | - (NSString *)method {
10 | return self.HTTPMethod;
11 | }
12 |
13 | - (NSDictionary *)headers {
14 | return self.allHTTPHeaderFields;
15 | }
16 |
17 | - (NSData *)body {
18 | if (self.HTTPBodyStream) {
19 | NSInputStream *stream = self.HTTPBodyStream;
20 | NSMutableData *data = [NSMutableData data];
21 | [stream open];
22 | size_t bufferSize = 4096;
23 | uint8_t *buffer = malloc(bufferSize);
24 | if (buffer == NULL) {
25 | [NSException raise:@"NocillaMallocFailure" format:@"Could not allocate %zu bytes to read HTTPBodyStream", bufferSize];
26 | }
27 | while ([stream hasBytesAvailable]) {
28 | NSInteger bytesRead = [stream read:buffer maxLength:bufferSize];
29 | if (bytesRead > 0) {
30 | NSData *readData = [NSData dataWithBytes:buffer length:bytesRead];
31 | [data appendData:readData];
32 | } else if (bytesRead < 0) {
33 | [NSException raise:@"NocillaStreamReadError" format:@"An error occurred while reading HTTPBodyStream (%ld)", (long)bytesRead];
34 | } else if (bytesRead == 0) {
35 | break;
36 | }
37 | }
38 | free(buffer);
39 | [stream close];
40 |
41 | return data;
42 | }
43 |
44 | return self.HTTPBody;
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.h:
--------------------------------------------------------------------------------
1 | //
2 | // LSNSURLSessionHook.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 08/01/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "LSHTTPClientHook.h"
12 |
13 | @interface LSNSURLSessionHook : LSHTTPClientHook
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Hooks/NSURLSession/LSNSURLSessionHook.m:
--------------------------------------------------------------------------------
1 | //
2 | // LSNSURLSessionHook.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 08/01/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "LSNSURLSessionHook.h"
10 | #import "LSHTTPStubURLProtocol.h"
11 | #import
12 |
13 | @implementation LSNSURLSessionHook
14 |
15 | - (void)load {
16 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
17 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];
18 | }
19 |
20 | - (void)unload {
21 | Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
22 | [self swizzleSelector:@selector(protocolClasses) fromClass:cls toClass:[self class]];
23 | }
24 |
25 | - (void)swizzleSelector:(SEL)selector fromClass:(Class)original toClass:(Class)stub {
26 |
27 | Method originalMethod = class_getInstanceMethod(original, selector);
28 | Method stubMethod = class_getInstanceMethod(stub, selector);
29 | if (!originalMethod || !stubMethod) {
30 | [NSException raise:NSInternalInconsistencyException format:@"Couldn't load NSURLSession hook."];
31 | }
32 | method_exchangeImplementations(originalMethod, stubMethod);
33 | }
34 |
35 | - (NSArray *)protocolClasses {
36 | return @[[LSHTTPStubURLProtocol class]];
37 | }
38 |
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/LSNocilla.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "Nocilla.h"
3 |
4 | @class LSStubRequest;
5 | @class LSStubResponse;
6 | @class LSHTTPClientHook;
7 | @protocol LSHTTPRequest;
8 |
9 | extern NSString * const LSUnexpectedRequest;
10 |
11 | @interface LSNocilla : NSObject
12 | + (LSNocilla *)sharedInstance;
13 |
14 | @property (nonatomic, strong, readonly) NSArray *stubbedRequests;
15 | @property (nonatomic, assign, readonly, getter = isStarted) BOOL started;
16 |
17 | - (void)start;
18 | - (void)stop;
19 | - (void)addStubbedRequest:(LSStubRequest *)request;
20 | - (void)clearStubs;
21 |
22 | - (void)registerHook:(LSHTTPClientHook *)hook;
23 |
24 | - (LSStubResponse *)responseForRequest:(id)request;
25 | @end
26 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/LSNocilla.m:
--------------------------------------------------------------------------------
1 | #import "LSNocilla.h"
2 | #import "LSNSURLHook.h"
3 | #import "LSStubRequest.h"
4 | #import "LSHTTPRequestDSLRepresentation.h"
5 | #import "LSASIHTTPRequestHook.h"
6 | #import "LSNSURLSessionHook.h"
7 | #import "LSASIHTTPRequestHook.h"
8 |
9 | NSString * const LSUnexpectedRequest = @"Unexpected Request";
10 |
11 | @interface LSNocilla ()
12 | @property (nonatomic, strong) NSMutableArray *mutableRequests;
13 | @property (nonatomic, strong) NSMutableArray *hooks;
14 | @property (nonatomic, assign, getter = isStarted) BOOL started;
15 |
16 | - (void)loadHooks;
17 | - (void)unloadHooks;
18 | @end
19 |
20 | static LSNocilla *sharedInstace = nil;
21 |
22 | @implementation LSNocilla
23 |
24 | + (LSNocilla *)sharedInstance {
25 | static dispatch_once_t onceToken;
26 | dispatch_once(&onceToken, ^{
27 | sharedInstace = [[self alloc] init];
28 | });
29 | return sharedInstace;
30 | }
31 |
32 | - (id)init {
33 | self = [super init];
34 | if (self) {
35 | _mutableRequests = [NSMutableArray array];
36 | _hooks = [NSMutableArray array];
37 | [self registerHook:[[LSNSURLHook alloc] init]];
38 | if (NSClassFromString(@"NSURLSession") != nil) {
39 | [self registerHook:[[LSNSURLSessionHook alloc] init]];
40 | }
41 | [self registerHook:[[LSASIHTTPRequestHook alloc] init]];
42 | }
43 | return self;
44 | }
45 |
46 | - (NSArray *)stubbedRequests {
47 | return [NSArray arrayWithArray:self.mutableRequests];
48 | }
49 |
50 | - (void)start {
51 | if (!self.isStarted){
52 | [self loadHooks];
53 | self.started = YES;
54 | }
55 | }
56 |
57 | - (void)stop {
58 | [self unloadHooks];
59 | [self clearStubs];
60 | self.started = NO;
61 | }
62 |
63 | - (void)addStubbedRequest:(LSStubRequest *)request {
64 | NSUInteger index = [self.mutableRequests indexOfObject:request];
65 |
66 | if (index == NSNotFound) {
67 | [self.mutableRequests addObject:request];
68 | return;
69 | }
70 |
71 | [self.mutableRequests replaceObjectAtIndex:index withObject:request];
72 | }
73 |
74 | - (void)clearStubs {
75 | [self.mutableRequests removeAllObjects];
76 | }
77 |
78 | - (LSStubResponse *)responseForRequest:(id)actualRequest {
79 | NSArray* requests = [LSNocilla sharedInstance].stubbedRequests;
80 |
81 | for(LSStubRequest *someStubbedRequest in requests) {
82 | if ([someStubbedRequest matchesRequest:actualRequest]) {
83 | return someStubbedRequest.response;
84 | }
85 | }
86 | [NSException raise:@"NocillaUnexpectedRequest" format:@"An unexpected HTTP request was fired.\n\nUse this snippet to stub the request:\n%@\n", [[[LSHTTPRequestDSLRepresentation alloc] initWithRequest:actualRequest] description]];
87 |
88 | return nil;
89 | }
90 |
91 | - (void)registerHook:(LSHTTPClientHook *)hook {
92 | if (![self hookWasRegistered:hook]) {
93 | [[self hooks] addObject:hook];
94 | }
95 | }
96 |
97 | - (BOOL)hookWasRegistered:(LSHTTPClientHook *)aHook {
98 | for (LSHTTPClientHook *hook in self.hooks) {
99 | if ([hook isMemberOfClass: [aHook class]]) {
100 | return YES;
101 | }
102 | }
103 | return NO;
104 | }
105 | #pragma mark - Private
106 | - (void)loadHooks {
107 | for (LSHTTPClientHook *hook in self.hooks) {
108 | [hook load];
109 | }
110 | }
111 |
112 | - (void)unloadHooks {
113 | for (LSHTTPClientHook *hook in self.hooks) {
114 | [hook unload];
115 | }
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSDataMatcher.h:
--------------------------------------------------------------------------------
1 | //
2 | // LSDataMatcher.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LSMatcher.h"
11 |
12 | @interface LSDataMatcher : LSMatcher
13 |
14 | - (instancetype)initWithData:(NSData *)data;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSDataMatcher.m:
--------------------------------------------------------------------------------
1 | //
2 | // LSDataMatcher.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "LSDataMatcher.h"
10 |
11 | @interface LSDataMatcher ()
12 |
13 | @property (nonatomic, copy) NSData *data;
14 |
15 | @end
16 |
17 | @implementation LSDataMatcher
18 |
19 | - (instancetype)initWithData:(NSData *)data {
20 | self = [super init];
21 |
22 | if (self) {
23 | _data = data;
24 | }
25 | return self;
26 | }
27 |
28 | - (BOOL)matchesData:(NSData *)data {
29 | return [self.data isEqualToData:data];
30 | }
31 |
32 |
33 | #pragma mark - Equality
34 |
35 | - (BOOL)isEqual:(id)object {
36 | if (self == object) {
37 | return YES;
38 | }
39 |
40 | if (![object isKindOfClass:[LSDataMatcher class]]) {
41 | return NO;
42 | }
43 |
44 | return [self.data isEqual:((LSDataMatcher *)object).data];
45 | }
46 |
47 | - (NSUInteger)hash {
48 | return self.data.hash;
49 | }
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSMatcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @class LSMatcher;
4 |
5 | @protocol LSMatcheable
6 |
7 | - (LSMatcher *)matcher;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSMatcher.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface LSMatcher : NSObject
4 |
5 | - (BOOL)matches:(NSString *)string;
6 |
7 | - (BOOL)matchesData:(NSData *)data;
8 |
9 | @end
10 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSMatcher.h"
2 |
3 | @implementation LSMatcher
4 |
5 | - (BOOL)matches:(NSString *)string {
6 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher matches:] is an abstract method" userInfo:nil];
7 | }
8 |
9 | - (BOOL)matchesData:(NSData *)data {
10 | return [self matches:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
11 | }
12 |
13 |
14 | #pragma mark - Equality
15 |
16 | - (BOOL)isEqual:(id)object {
17 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher isEqual:] is an abstract method" userInfo:nil];
18 | }
19 |
20 | - (NSUInteger)hash {
21 | @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"[LSMatcher hash] an abstract method" userInfo:nil];
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSRegexMatcher.h:
--------------------------------------------------------------------------------
1 | #import "LSMatcher.h"
2 |
3 | @interface LSRegexMatcher : LSMatcher
4 |
5 | - (instancetype)initWithRegex:(NSRegularExpression *)regex;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSRegexMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSRegexMatcher.h"
2 |
3 | @interface LSRegexMatcher ()
4 | @property (nonatomic, strong) NSRegularExpression *regex;
5 | @end
6 |
7 | @implementation LSRegexMatcher
8 |
9 | - (instancetype)initWithRegex:(NSRegularExpression *)regex {
10 | self = [super init];
11 | if (self) {
12 | _regex = regex;
13 | }
14 | return self;
15 | }
16 |
17 | - (BOOL)matches:(NSString *)string {
18 | return [self.regex numberOfMatchesInString:string options:0 range:NSMakeRange(0, string.length)] > 0;
19 | }
20 |
21 |
22 | #pragma mark - Equality
23 |
24 | - (BOOL)isEqual:(id)object {
25 | if (self == object) {
26 | return YES;
27 | }
28 |
29 | if (![object isKindOfClass:[LSRegexMatcher class]]) {
30 | return NO;
31 | }
32 |
33 | return [self.regex isEqual:((LSRegexMatcher *)object).regex];
34 | }
35 |
36 | - (NSUInteger)hash {
37 | return self.regex.hash;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSStringMatcher.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcher.h"
3 |
4 | @interface LSStringMatcher : LSMatcher
5 |
6 | - (instancetype)initWithString:(NSString *)string;
7 |
8 | @end
9 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/LSStringMatcher.m:
--------------------------------------------------------------------------------
1 | #import "LSStringMatcher.h"
2 |
3 | @interface LSStringMatcher ()
4 |
5 | @property (nonatomic, copy) NSString *string;
6 |
7 | @end
8 |
9 | @implementation LSStringMatcher
10 |
11 | - (instancetype)initWithString:(NSString *)string {
12 | self = [super init];
13 | if (self) {
14 | _string = string;
15 | }
16 | return self;
17 | }
18 |
19 | - (BOOL)matches:(NSString *)string {
20 | return [self.string isEqualToString:string];
21 | }
22 |
23 |
24 | #pragma mark - Equality
25 |
26 | - (BOOL)isEqual:(id)object {
27 | if (self == object) {
28 | return YES;
29 | }
30 |
31 | if (![object isKindOfClass:[LSStringMatcher class]]) {
32 | return NO;
33 | }
34 |
35 | return [self.string isEqualToString:((LSStringMatcher *)object).string];
36 | }
37 |
38 | - (NSUInteger)hash {
39 | return self.string.hash;
40 | }
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSData+Matcheable.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+Matcheable.h
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "LSMatcheable.h"
11 |
12 | @interface NSData (Matcheable)
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSData+Matcheable.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+Matcheable.m
3 | // Nocilla
4 | //
5 | // Created by Luis Solano Bonet on 09/11/14.
6 | // Copyright (c) 2014 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import "NSData+Matcheable.h"
10 | #import "LSDataMatcher.h"
11 |
12 | @implementation NSData (Matcheable)
13 |
14 | - (LSMatcher *)matcher {
15 | return [[LSDataMatcher alloc] initWithData:self];
16 | }
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSRegularExpression+Matcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcheable.h"
3 |
4 | @interface NSRegularExpression (Matcheable)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSRegularExpression+Matcheable.m:
--------------------------------------------------------------------------------
1 | #import "NSRegularExpression+Matcheable.h"
2 | #import "LSRegexMatcher.h"
3 |
4 | @implementation NSRegularExpression (Matcheable)
5 |
6 | - (LSMatcher *)matcher {
7 | return [[LSRegexMatcher alloc] initWithRegex:self];
8 | }
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSString+Matcheable.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSMatcheable.h"
3 |
4 | @interface NSString (Matcheable)
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Matchers/NSString+Matcheable.m:
--------------------------------------------------------------------------------
1 | #import "NSString+Matcheable.h"
2 | #import "LSStringMatcher.h"
3 |
4 | @implementation NSString (Matcheable)
5 |
6 | - (LSMatcher *)matcher {
7 | return [[LSStringMatcher alloc] initWithString:self];
8 | }
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Model/LSHTTPBody.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPBody
4 | - (NSData *)data;
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Model/LSHTTPRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPRequest
4 |
5 | @property (nonatomic, strong, readonly) NSURL *url;
6 | @property (nonatomic, strong, readonly) NSString *method;
7 | @property (nonatomic, strong, readonly) NSDictionary *headers;
8 | @property (nonatomic, strong, readonly) NSData *body;
9 |
10 | @end
11 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Model/LSHTTPResponse.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @protocol LSHTTPResponse
4 | @property (nonatomic, assign, readonly) NSInteger statusCode;
5 | @property (nonatomic, strong, readonly) NSDictionary *headers;
6 | @property (nonatomic, strong, readonly) NSData *body;
7 | @end
8 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Nocilla.h:
--------------------------------------------------------------------------------
1 | //
2 | // Nocilla.h
3 | // Nocilla
4 | //
5 | // Created by Robert Böhnke on 26/03/15.
6 | // Copyright (c) 2015 Luis Solano Bonet. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Nocilla.
12 | FOUNDATION_EXPORT double NocillaVersionNumber;
13 |
14 | //! Project version string for Nocilla.
15 | FOUNDATION_EXPORT const unsigned char NocillaVersionString[];
16 |
17 | #import
18 | #import
19 | #import
20 | #import
21 | #import
22 | #import
23 | #import
24 | #import
25 | #import
26 | #import
27 | #import
28 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Stubs/LSStubRequest.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSStubResponse.h"
3 | #import "LSHTTPRequest.h"
4 |
5 |
6 | @class LSMatcher;
7 | @class LSStubRequest;
8 | @class LSStubResponse;
9 |
10 | @interface LSStubRequest : NSObject
11 | @property (nonatomic, strong, readonly) NSString *method;
12 | @property (nonatomic, strong, readonly) LSMatcher *urlMatcher;
13 | @property (nonatomic, strong, readonly) NSDictionary *headers;
14 | @property (nonatomic, strong, readwrite) LSMatcher *body;
15 |
16 | @property (nonatomic, strong) LSStubResponse *response;
17 |
18 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url;
19 | - (instancetype)initWithMethod:(NSString *)method urlMatcher:(LSMatcher *)urlMatcher;
20 |
21 | - (void)setHeader:(NSString *)header value:(NSString *)value;
22 |
23 | - (BOOL)matchesRequest:(id)request;
24 | @end
25 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Stubs/LSStubRequest.m:
--------------------------------------------------------------------------------
1 | #import "LSStubRequest.h"
2 | #import "LSMatcher.h"
3 | #import "NSString+Matcheable.h"
4 |
5 | @interface LSStubRequest ()
6 | @property (nonatomic, strong, readwrite) NSString *method;
7 | @property (nonatomic, strong, readwrite) LSMatcher *urlMatcher;
8 | @property (nonatomic, strong, readwrite) NSMutableDictionary *mutableHeaders;
9 |
10 | -(BOOL)matchesMethod:(id)request;
11 | -(BOOL)matchesURL:(id)request;
12 | -(BOOL)matchesHeaders:(id)request;
13 | -(BOOL)matchesBody:(id)request;
14 | @end
15 |
16 | @implementation LSStubRequest
17 |
18 | - (instancetype)initWithMethod:(NSString *)method url:(NSString *)url {
19 | return [self initWithMethod:method urlMatcher:[url matcher]];
20 | }
21 |
22 | - (instancetype)initWithMethod:(NSString *)method urlMatcher:(LSMatcher *)urlMatcher; {
23 | self = [super init];
24 | if (self) {
25 | self.method = method;
26 | self.urlMatcher = urlMatcher;
27 | self.mutableHeaders = [NSMutableDictionary dictionary];
28 | }
29 | return self;
30 | }
31 |
32 | - (void)setHeader:(NSString *)header value:(NSString *)value {
33 | [self.mutableHeaders setValue:value forKey:header];
34 | }
35 |
36 | - (NSDictionary *)headers {
37 | return [NSDictionary dictionaryWithDictionary:self.mutableHeaders];;
38 | }
39 |
40 | - (NSString *)description {
41 | return [NSString stringWithFormat:@"StubRequest:\nMethod: %@\nURL: %@\nHeaders: %@\nBody: %@\nResponse: %@",
42 | self.method,
43 | self.urlMatcher,
44 | self.headers,
45 | self.body,
46 | self.response];
47 | }
48 |
49 | - (LSStubResponse *)response {
50 | if (!_response) {
51 | _response = [[LSStubResponse alloc] initDefaultResponse];
52 | }
53 | return _response;
54 |
55 | }
56 |
57 | - (BOOL)matchesRequest:(id)request {
58 | if ([self matchesMethod:request]
59 | && [self matchesURL:request]
60 | && [self matchesHeaders:request]
61 | && [self matchesBody:request]
62 | ) {
63 | return YES;
64 | }
65 | return NO;
66 | }
67 |
68 | -(BOOL)matchesMethod:(id)request {
69 | if (!self.method || [self.method isEqualToString:request.method]) {
70 | return YES;
71 | }
72 | return NO;
73 | }
74 |
75 | -(BOOL)matchesURL:(id)request {
76 | return [self.urlMatcher matches:[request.url absoluteString]];
77 | }
78 |
79 | -(BOOL)matchesHeaders:(id)request {
80 | for (NSString *header in self.headers) {
81 | if (![[request.headers objectForKey:header] isEqualToString:[self.headers objectForKey:header]]) {
82 | return NO;
83 | }
84 | }
85 | return YES;
86 | }
87 |
88 | -(BOOL)matchesBody:(id)request {
89 | NSData *reqBody = request.body;
90 | if (!self.body || [self.body matchesData:reqBody]) {
91 | return YES;
92 | }
93 | return NO;
94 | }
95 |
96 |
97 | #pragma mark - Equality
98 |
99 | - (BOOL)isEqual:(id)object {
100 | if (self == object) {
101 | return YES;
102 | }
103 |
104 | if (![object isKindOfClass:[LSStubRequest class]]) {
105 | return NO;
106 | }
107 |
108 | return [self isEqualToStubRequest:object];
109 | }
110 |
111 | - (BOOL)isEqualToStubRequest:(LSStubRequest *)stubRequest {
112 | if (!stubRequest) {
113 | return NO;
114 | }
115 |
116 | BOOL methodEqual = [self.method isEqualToString:stubRequest.method];
117 | BOOL urlMatcherEqual = [self.urlMatcher isEqual:stubRequest.urlMatcher];
118 | BOOL headersEqual = [self.headers isEqual:stubRequest.headers];
119 | BOOL bodyEqual = (self.body == nil && stubRequest.body == nil) || [self.body isEqual:stubRequest.body];
120 |
121 | return methodEqual && urlMatcherEqual && headersEqual && bodyEqual;
122 | }
123 |
124 | - (NSUInteger)hash {
125 | return self.method.hash ^ self.urlMatcher.hash ^ self.headers.hash ^ self.body.hash;
126 | }
127 |
128 | @end
129 |
130 |
131 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Stubs/LSStubResponse.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "LSHTTPResponse.h"
3 |
4 | @interface LSStubResponse : NSObject
5 |
6 | @property (nonatomic, assign, readonly) NSInteger statusCode;
7 | @property (nonatomic, strong) NSData *body;
8 | @property (nonatomic, strong, readonly) NSDictionary *headers;
9 |
10 | @property (nonatomic, assign, readonly) BOOL shouldFail;
11 | @property (nonatomic, strong, readonly) NSError *error;
12 |
13 | - (id)initWithError:(NSError *)error;
14 | - (id)initWithStatusCode:(NSInteger)statusCode;
15 | - (id)initWithRawResponse:(NSData *)rawResponseData;
16 | - (id)initDefaultResponse;
17 | - (void)setHeader:(NSString *)header value:(NSString *)value;
18 | @end
19 |
--------------------------------------------------------------------------------
/Pods/Nocilla/Nocilla/Stubs/LSStubResponse.m:
--------------------------------------------------------------------------------
1 | #import "LSStubResponse.h"
2 |
3 | @interface LSStubResponse ()
4 | @property (nonatomic, assign, readwrite) NSInteger statusCode;
5 | @property (nonatomic, strong) NSMutableDictionary *mutableHeaders;
6 | @property (nonatomic, assign) UInt64 offset;
7 | @property (nonatomic, assign, getter = isDone) BOOL done;
8 | @property (nonatomic, assign) BOOL shouldFail;
9 | @property (nonatomic, strong) NSError *error;
10 | @end
11 |
12 | @implementation LSStubResponse
13 |
14 | #pragma Initializers
15 | - (id)initDefaultResponse {
16 | self = [super init];
17 | if (self) {
18 | self.shouldFail = NO;
19 |
20 | self.statusCode = 200;
21 | self.mutableHeaders = [NSMutableDictionary dictionary];
22 | self.body = [@"" dataUsingEncoding:NSUTF8StringEncoding];
23 | }
24 | return self;
25 | }
26 |
27 |
28 | - (id)initWithError:(NSError *)error {
29 | self = [super init];
30 | if (self) {
31 | self.shouldFail = YES;
32 | self.error = error;
33 | }
34 | return self;
35 | }
36 |
37 | -(id)initWithStatusCode:(NSInteger)statusCode {
38 | self = [super init];
39 | if (self) {
40 | self.shouldFail = NO;
41 | self.statusCode = statusCode;
42 | self.mutableHeaders = [NSMutableDictionary dictionary];
43 | self.body = [@"" dataUsingEncoding:NSUTF8StringEncoding];
44 | }
45 | return self;
46 | }
47 |
48 | - (id)initWithRawResponse:(NSData *)rawResponseData {
49 | self = [self initDefaultResponse];
50 | if (self) {
51 | CFHTTPMessageRef httpMessage = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, FALSE);
52 | if (httpMessage) {
53 | CFHTTPMessageAppendBytes(httpMessage, [rawResponseData bytes], [rawResponseData length]);
54 |
55 | self.body = rawResponseData; // By default
56 |
57 | if (CFHTTPMessageIsHeaderComplete(httpMessage)) {
58 | self.statusCode = (NSInteger)CFHTTPMessageGetResponseStatusCode(httpMessage);
59 | self.mutableHeaders = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)CFHTTPMessageCopyAllHeaderFields(httpMessage)];
60 | self.body = (__bridge_transfer NSData *)CFHTTPMessageCopyBody(httpMessage);
61 | }
62 | CFRelease(httpMessage);
63 | }
64 | }
65 | return self;
66 | }
67 |
68 | - (void)setHeader:(NSString *)header value:(NSString *)value {
69 | [self.mutableHeaders setValue:value forKey:header];
70 | }
71 | - (NSDictionary *)headers {
72 | return [NSDictionary dictionaryWithDictionary:self.mutableHeaders];
73 | }
74 |
75 | - (NSString *)description {
76 | return [NSString stringWithFormat:@"StubRequest:\nStatus Code: %ld\nHeaders: %@\nBody: %@",
77 | (long)self.statusCode,
78 | self.mutableHeaders,
79 | self.body];
80 | }
81 | @end
82 |
--------------------------------------------------------------------------------
/Pods/Nocilla/README.md:
--------------------------------------------------------------------------------
1 | # Nocilla [](https://travis-ci.org/luisobo/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)[](http://cocoadocs.org/docsets/Nocilla)
2 |
3 | Stunning HTTP stubbing for iOS and OS X. Testing HTTP requests has never been easier.
4 |
5 | This library was inspired by [WebMock](https://github.com/bblimke/webmock) and it's using [this approach](http://www.infinite-loop.dk/blog/2011/09/using-nsurlprotocol-for-injecting-test-data/) to stub the requests.
6 |
7 | ## Features
8 | * Stub HTTP and HTTPS requests in your unit tests.
9 | * Supports NSURLConnection, NSURLSession and ASIHTTPRequest.
10 | * Awesome DSL that will improve the readability and maintainability of your tests.
11 | * Match requests with regular expressions.
12 | * Stub requests with errors.
13 | * Tested.
14 | * Fast.
15 | * Extendable to support more HTTP libraries.
16 |
17 | ## Installation
18 | ### As a [CocoaPod](http://cocoapods.org/)
19 | Just add this to your Podfile
20 | ```ruby
21 | pod 'Nocilla'
22 | ```
23 |
24 | ### Other approaches
25 | * You should be able to add Nocilla to you source tree. If you are using git, consider using a `git submodule`
26 |
27 | ## Usage
28 | _Yes, the following code is valid Objective-C, or at least, it should be_
29 |
30 | The following examples are described using [Kiwi](https://github.com/kiwi-bdd/Kiwi)
31 |
32 | ### Common parts
33 | Until Nocilla can hook directly into Kiwi, you will have to include the following snippet in the specs you want to use Nocilla:
34 |
35 | ```objc
36 | #import "Kiwi.h"
37 | #import "Nocilla.h"
38 | SPEC_BEGIN(ExampleSpec)
39 | beforeAll(^{
40 | [[LSNocilla sharedInstance] start];
41 | });
42 | afterAll(^{
43 | [[LSNocilla sharedInstance] stop];
44 | });
45 | afterEach(^{
46 | [[LSNocilla sharedInstance] clearStubs];
47 | });
48 |
49 | it(@"should do something", ^{
50 | // Stub here!
51 | });
52 | SPEC_END
53 | ```
54 |
55 | ### Stubbing requests
56 | #### Stubbing a simple request
57 | It will return the default response, which is a 200 and an empty body.
58 |
59 | ```objc
60 | stubRequest(@"GET", @"http://www.google.com");
61 | ```
62 |
63 | #### Stubbing requests with regular expressions
64 | ```objc
65 | stubRequest(@"GET", @"^http://(.*?)\\.example\\.com/v1/dogs\\.json".regex);
66 | ```
67 |
68 |
69 | #### Stubbing a request with a particular header
70 |
71 | ```objc
72 | stubRequest(@"GET", @"https://api.example.com").
73 | withHeader(@"Accept", @"application/json");
74 | ```
75 |
76 | #### Stubbing a request with multiple headers
77 |
78 | Using the `withHeaders` method makes sense with the Objective-C literals, but it accepts an NSDictionary.
79 |
80 | ```objc
81 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
82 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"});
83 | ```
84 |
85 | #### Stubbing a request with a particular body
86 |
87 | ```objc
88 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
89 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
90 | withBody(@"{\"name\":\"foo\"}");
91 | ```
92 |
93 | You can also use `NSData` for the request body:
94 |
95 | ```objc
96 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
97 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
98 | withBody([@"foo" dataUsingEncoding:NSUTF8StringEncoding]);
99 | ```
100 |
101 | It even works with regular expressions!
102 |
103 | ```objc
104 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
105 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
106 | withBody(@"^The body start with this".regex);
107 | ```
108 |
109 | #### Returning a specific status code
110 | ```objc
111 | stubRequest(@"GET", @"http://www.google.com").andReturn(404);
112 | ```
113 |
114 | #### Returning a specific status code and header
115 | The same approch here, you can use `withHeader` or `withHeaders`
116 |
117 | ```objc
118 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
119 | andReturn(201).
120 | withHeaders(@{@"Content-Type": @"application/json"});
121 | ```
122 |
123 | #### Returning a specific status code, headers and body
124 | ```objc
125 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
126 | andReturn(201).
127 | withHeaders(@{@"Content-Type": @"application/json"}).
128 | withBody(@"{\"ok\":true}");
129 | ```
130 |
131 | You can also use `NSData` for the response body:
132 |
133 | ```objc
134 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
135 | andReturn(201).
136 | withHeaders(@{@"Content-Type": @"application/json"}).
137 | withBody([@"bar" dataUsingEncoding:NSUTF8StringEncoding]);
138 | ```
139 |
140 | #### Returning raw responses recorded with `curl -is`
141 | `curl -is http://api.example.com/dogs.json > /tmp/example_curl_-is_output.txt`
142 |
143 | ```objc
144 | stubRequest(@"GET", @"https://api.example.com/dogs.json").
145 | andReturnRawResponse([NSData dataWithContentsOfFile:@"/tmp/example_curl_-is_output.txt"]);
146 | ```
147 |
148 | #### All together
149 | ```objc
150 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
151 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
152 | withBody(@"{\"name\":\"foo\"}").
153 | andReturn(201).
154 | withHeaders(@{@"Content-Type": @"application/json"}).
155 | withBody(@"{\"ok\":true}");
156 | ```
157 |
158 | #### Making a request fail
159 | This will call the failure handler (callback, delegate... whatever your HTTP client uses) with the specified error.
160 |
161 | ```objc
162 | stubRequest(@"POST", @"https://api.example.com/dogs.json").
163 | withHeaders(@{@"Accept": @"application/json", @"X-CUSTOM-HEADER": @"abcf2fbc6abgf"}).
164 | withBody(@"{\"name\":\"foo\"}").
165 | andFailWithError([NSError errorWithDomain:@"foo" code:123 userInfo:nil]);
166 | ```
167 |
168 | #### Replacing a request stub
169 |
170 | If you need to change the response of a single request, simply re-stub the request:
171 |
172 | ```objc
173 | stubRequest(@"POST", @"https://api.example.com/authorize/").
174 | andReturn(401);
175 |
176 | // Some test expectation...
177 |
178 | stubRequest(@"POST", @"https://api.example.com/authorize/").
179 | andReturn(200);
180 | ```
181 |
182 | ### Unexpected requests
183 | If some request is made but it wasn't stubbed, Nocilla won't let that request hit the real world. In that case your test should fail.
184 | At this moment Nocilla will raise an exception with a meaningful message about the error and how to solve it, including a snippet of code on how to stub the unexpected request.
185 |
186 | ### Testing asynchronous requests
187 | When testing asynchrounous requests your request will be sent on a different thread from the one on which your test is executed. It is important to keep this in mind, and design your test in such a way that is has enough time to finish. For instance ```tearDown()``` when using ```XCTest``` and ```afterEach()``` when using [Quick](https://github.com/Quick/Quick) and [Nimble](https://github.com/Quick/Nimble) will cause the request never to complete.
188 |
189 |
190 | ## Who uses Nocilla.
191 |
192 | ### Submit a PR to add your company here!
193 |
194 | - [MessageBird](https://www.messagebird.com)
195 | - [Groupon](http://www.groupon.com)
196 | - [Pixable](http://www.pixable.com)
197 | - [Jackthreads](https://www.jackthreads.com)
198 | - [ShopKeep](http://www.shopkeep.com)
199 | - [Venmo](https://www.venmo.com)
200 | - [Lighthouse](http://www.lighthouselabs.co.uk)
201 | - [GE Digital](http://www.ge.com/digital/)
202 |
203 | ## Other alternatives
204 | * [ILTesting](https://github.com/InfiniteLoopDK/ILTesting)
205 | * [OHHTTPStubs](https://github.com/AliSoftware/OHHTTPStubs)
206 |
207 | ## Contributing
208 |
209 | 1. Fork it
210 | 2. Create your feature branch
211 | 3. Commit your changes
212 | 4. Push to the branch
213 | 5. Create new Pull Request
214 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Alamofire-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Alamofire : NSObject
3 | @end
4 | @implementation PodsDummy_Alamofire
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double AlamofireVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Alamofire.modulemap:
--------------------------------------------------------------------------------
1 | framework module Alamofire {
2 | umbrella header "Alamofire-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Alamofire.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
9 | SKIP_INSTALL = YES
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Alamofire/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 4.0.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 0.11.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Nocilla-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Nocilla : NSObject
3 | @end
4 | @implementation PodsDummy_Nocilla
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Nocilla-prefix.pch:
--------------------------------------------------------------------------------
1 | #ifdef __OBJC__
2 | #import
3 | #endif
4 |
5 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Nocilla-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "NSData+Nocilla.h"
4 | #import "NSString+Nocilla.h"
5 | #import "LSStubRequestDSL.h"
6 | #import "LSStubResponseDSL.h"
7 | #import "LSNocilla.h"
8 | #import "LSMatcheable.h"
9 | #import "LSMatcher.h"
10 | #import "NSData+Matcheable.h"
11 | #import "NSRegularExpression+Matcheable.h"
12 | #import "NSString+Matcheable.h"
13 | #import "LSHTTPBody.h"
14 | #import "Nocilla.h"
15 |
16 | FOUNDATION_EXPORT double NocillaVersionNumber;
17 | FOUNDATION_EXPORT const unsigned char NocillaVersionString[];
18 |
19 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Nocilla.modulemap:
--------------------------------------------------------------------------------
1 | framework module Nocilla {
2 | umbrella header "Nocilla-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Nocilla/Nocilla.xcconfig:
--------------------------------------------------------------------------------
1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Nocilla
2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
4 | OTHER_LDFLAGS = -framework "CFNetwork"
5 | PODS_BUILD_DIR = $BUILD_DIR
6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
7 | PODS_ROOT = ${SRCROOT}
8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
9 | SKIP_INSTALL = YES
10 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## Alamofire
5 |
6 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
7 |
8 | Permission is hereby granted, free of charge, to any person obtaining a copy
9 | of this software and associated documentation files (the "Software"), to deal
10 | in the Software without restriction, including without limitation the rights
11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | copies of the Software, and to permit persons to whom the Software is
13 | furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in
16 | all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | THE SOFTWARE.
25 |
26 | Generated by CocoaPods - https://cocoapods.org
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
18 |
19 | Permission is hereby granted, free of charge, to any person obtaining a copy
20 | of this software and associated documentation files (the "Software"), to deal
21 | in the Software without restriction, including without limitation the rights
22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | copies of the Software, and to permit persons to whom the Software is
24 | furnished to do so, subject to the following conditions:
25 |
26 | The above copyright notice and this permission notice shall be included in
27 | all copies or substantial portions of the Software.
28 |
29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
35 | THE SOFTWARE.
36 |
37 | License
38 | MIT
39 | Title
40 | Alamofire
41 | Type
42 | PSGroupSpecifier
43 |
44 |
45 | FooterText
46 | Generated by CocoaPods - https://cocoapods.org
47 | Title
48 |
49 | Type
50 | PSGroupSpecifier
51 |
52 |
53 | StringsTable
54 | Acknowledgements
55 | Title
56 | Acknowledgements
57 |
58 |
59 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_GithubPilot : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_GithubPilot
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | *)
22 | TARGET_DEVICE_ARGS="--target-device mac"
23 | ;;
24 | esac
25 |
26 | realpath() {
27 | DIRECTORY="$(cd "${1%/*}" && pwd)"
28 | FILENAME="${1##*/}"
29 | echo "$DIRECTORY/$FILENAME"
30 | }
31 |
32 | install_resource()
33 | {
34 | if [[ "$1" = /* ]] ; then
35 | RESOURCE_PATH="$1"
36 | else
37 | RESOURCE_PATH="${PODS_ROOT}/$1"
38 | fi
39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
40 | cat << EOM
41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
42 | EOM
43 | exit 1
44 | fi
45 | case $RESOURCE_PATH in
46 | *.storyboard)
47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
49 | ;;
50 | *.xib)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.framework)
55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
59 | ;;
60 | *.xcdatamodel)
61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
63 | ;;
64 | *.xcdatamodeld)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
67 | ;;
68 | *.xcmappingmodel)
69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
71 | ;;
72 | *.xcassets)
73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
75 | ;;
76 | *)
77 | echo "$RESOURCE_PATH"
78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
79 | ;;
80 | esac
81 | }
82 |
83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | fi
89 | rm -f "$RESOURCES_TO_COPY"
90 |
91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
92 | then
93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
95 | while read line; do
96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
97 | XCASSET_FILES+=("$line")
98 | fi
99 | done <<<"$OTHER_XCASSETS"
100 |
101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
102 | fi
103 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double Pods_GithubPilotVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char Pods_GithubPilotVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}/Pods
11 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_GithubPilot {
2 | umbrella header "Pods-GithubPilot-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilot/Pods-GithubPilot.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire"
3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers"
6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire"
7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
8 | PODS_BUILD_DIR = $BUILD_DIR
9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
10 | PODS_ROOT = ${SRCROOT}/Pods
11 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-acknowledgements.markdown:
--------------------------------------------------------------------------------
1 | # Acknowledgements
2 | This application makes use of the following third party libraries:
3 |
4 | ## Nocilla
5 |
6 | Copyright (c) 2012 Luis Solano Bonet
7 | MIT License
8 |
9 | Permission is hereby granted, free of charge, to any person obtaining
10 | a copy of this software and associated documentation files (the
11 | "Software"), to deal in the Software without restriction, including
12 | without limitation the rights to use, copy, modify, merge, publish,
13 | distribute, sublicense, and/or sell copies of the Software, and to
14 | permit persons to whom the Software is furnished to do so, subject to
15 | the following conditions:
16 |
17 | The above copyright notice and this permission notice shall be
18 | included in all copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 |
28 | ## Alamofire
29 |
30 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
31 |
32 | Permission is hereby granted, free of charge, to any person obtaining a copy
33 | of this software and associated documentation files (the "Software"), to deal
34 | in the Software without restriction, including without limitation the rights
35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36 | copies of the Software, and to permit persons to whom the Software is
37 | furnished to do so, subject to the following conditions:
38 |
39 | The above copyright notice and this permission notice shall be included in
40 | all copies or substantial portions of the Software.
41 |
42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48 | THE SOFTWARE.
49 |
50 | Generated by CocoaPods - https://cocoapods.org
51 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-acknowledgements.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreferenceSpecifiers
6 |
7 |
8 | FooterText
9 | This application makes use of the following third party libraries:
10 | Title
11 | Acknowledgements
12 | Type
13 | PSGroupSpecifier
14 |
15 |
16 | FooterText
17 | Copyright (c) 2012 Luis Solano Bonet
18 | MIT License
19 |
20 | Permission is hereby granted, free of charge, to any person obtaining
21 | a copy of this software and associated documentation files (the
22 | "Software"), to deal in the Software without restriction, including
23 | without limitation the rights to use, copy, modify, merge, publish,
24 | distribute, sublicense, and/or sell copies of the Software, and to
25 | permit persons to whom the Software is furnished to do so, subject to
26 | the following conditions:
27 |
28 | The above copyright notice and this permission notice shall be
29 | included in all copies or substantial portions of the Software.
30 |
31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 | License
39 | MIT
40 | Title
41 | Nocilla
42 | Type
43 | PSGroupSpecifier
44 |
45 |
46 | FooterText
47 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
48 |
49 | Permission is hereby granted, free of charge, to any person obtaining a copy
50 | of this software and associated documentation files (the "Software"), to deal
51 | in the Software without restriction, including without limitation the rights
52 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
53 | copies of the Software, and to permit persons to whom the Software is
54 | furnished to do so, subject to the following conditions:
55 |
56 | The above copyright notice and this permission notice shall be included in
57 | all copies or substantial portions of the Software.
58 |
59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
62 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
63 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
64 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
65 | THE SOFTWARE.
66 |
67 | License
68 | MIT
69 | Title
70 | Alamofire
71 | Type
72 | PSGroupSpecifier
73 |
74 |
75 | FooterText
76 | Generated by CocoaPods - https://cocoapods.org
77 | Title
78 |
79 | Type
80 | PSGroupSpecifier
81 |
82 |
83 | StringsTable
84 | Acknowledgements
85 | Title
86 | Acknowledgements
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-dummy.m:
--------------------------------------------------------------------------------
1 | #import
2 | @interface PodsDummy_Pods_GithubPilotTests : NSObject
3 | @end
4 | @implementation PodsDummy_Pods_GithubPilotTests
5 | @end
6 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-frameworks.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
6 |
7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
8 |
9 | install_framework()
10 | {
11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
12 | local source="${BUILT_PRODUCTS_DIR}/$1"
13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
15 | elif [ -r "$1" ]; then
16 | local source="$1"
17 | fi
18 |
19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
20 |
21 | if [ -L "${source}" ]; then
22 | echo "Symlinked..."
23 | source="$(readlink "${source}")"
24 | fi
25 |
26 | # use filter instead of exclude so missing patterns dont' throw errors
27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
29 |
30 | local basename
31 | basename="$(basename -s .framework "$1")"
32 | binary="${destination}/${basename}.framework/${basename}"
33 | if ! [ -r "$binary" ]; then
34 | binary="${destination}/${basename}"
35 | fi
36 |
37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device
38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
39 | strip_invalid_archs "$binary"
40 | fi
41 |
42 | # Resign the code if required by the build settings to avoid unstable apps
43 | code_sign_if_enabled "${destination}/$(basename "$1")"
44 |
45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
47 | local swift_runtime_libs
48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]})
49 | for lib in $swift_runtime_libs; do
50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
52 | code_sign_if_enabled "${destination}/${lib}"
53 | done
54 | fi
55 | }
56 |
57 | # Signs a framework with the provided identity
58 | code_sign_if_enabled() {
59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
60 | # Use the current code_sign_identitiy
61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\""
63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"
64 | fi
65 | }
66 |
67 | # Strip invalid architectures
68 | strip_invalid_archs() {
69 | binary="$1"
70 | # Get architectures for current file
71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
72 | stripped=""
73 | for arch in $archs; do
74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
75 | # Strip non-valid architectures in-place
76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1
77 | stripped="$stripped $arch"
78 | fi
79 | done
80 | if [[ "$stripped" ]]; then
81 | echo "Stripped $binary of architectures:$stripped"
82 | fi
83 | }
84 |
85 |
86 | if [[ "$CONFIGURATION" == "Debug" ]]; then
87 | install_framework "$BUILT_PRODUCTS_DIR/Nocilla/Nocilla.framework"
88 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework"
89 | fi
90 | if [[ "$CONFIGURATION" == "Release" ]]; then
91 | install_framework "$BUILT_PRODUCTS_DIR/Nocilla/Nocilla.framework"
92 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework"
93 | fi
94 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-resources.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
5 |
6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
7 | > "$RESOURCES_TO_COPY"
8 |
9 | XCASSET_FILES=()
10 |
11 | case "${TARGETED_DEVICE_FAMILY}" in
12 | 1,2)
13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
14 | ;;
15 | 1)
16 | TARGET_DEVICE_ARGS="--target-device iphone"
17 | ;;
18 | 2)
19 | TARGET_DEVICE_ARGS="--target-device ipad"
20 | ;;
21 | *)
22 | TARGET_DEVICE_ARGS="--target-device mac"
23 | ;;
24 | esac
25 |
26 | realpath() {
27 | DIRECTORY="$(cd "${1%/*}" && pwd)"
28 | FILENAME="${1##*/}"
29 | echo "$DIRECTORY/$FILENAME"
30 | }
31 |
32 | install_resource()
33 | {
34 | if [[ "$1" = /* ]] ; then
35 | RESOURCE_PATH="$1"
36 | else
37 | RESOURCE_PATH="${PODS_ROOT}/$1"
38 | fi
39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then
40 | cat << EOM
41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script.
42 | EOM
43 | exit 1
44 | fi
45 | case $RESOURCE_PATH in
46 | *.storyboard)
47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
49 | ;;
50 | *.xib)
51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
53 | ;;
54 | *.framework)
55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
59 | ;;
60 | *.xcdatamodel)
61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
63 | ;;
64 | *.xcdatamodeld)
65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
67 | ;;
68 | *.xcmappingmodel)
69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
71 | ;;
72 | *.xcassets)
73 | ABSOLUTE_XCASSET_FILE=$(realpath "$RESOURCE_PATH")
74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
75 | ;;
76 | *)
77 | echo "$RESOURCE_PATH"
78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
79 | ;;
80 | esac
81 | }
82 |
83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
88 | fi
89 | rm -f "$RESOURCES_TO_COPY"
90 |
91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
92 | then
93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets).
94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
95 | while read line; do
96 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then
97 | XCASSET_FILES+=("$line")
98 | fi
99 | done <<<"$OTHER_XCASSETS"
100 |
101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
102 | fi
103 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests-umbrella.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 |
4 | FOUNDATION_EXPORT double Pods_GithubPilotTestsVersionNumber;
5 | FOUNDATION_EXPORT const unsigned char Pods_GithubPilotTestsVersionString[];
6 |
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests.debug.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Nocilla"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nocilla/Nocilla.framework/Headers"
7 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Nocilla"
8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
9 | PODS_BUILD_DIR = $BUILD_DIR
10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests.modulemap:
--------------------------------------------------------------------------------
1 | framework module Pods_GithubPilotTests {
2 | umbrella header "Pods-GithubPilotTests-umbrella.h"
3 |
4 | export *
5 | module * { export * }
6 | }
7 |
--------------------------------------------------------------------------------
/Pods/Target Support Files/Pods-GithubPilotTests/Pods-GithubPilotTests.release.xcconfig:
--------------------------------------------------------------------------------
1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
2 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES
3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/Nocilla"
4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
6 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nocilla/Nocilla.framework/Headers"
7 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "Nocilla"
8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
9 | PODS_BUILD_DIR = $BUILD_DIR
10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
11 | PODS_ROOT = ${SRCROOT}/Pods
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GithubPilot - Github API V3 Swifty Wrapper
2 | [](https://travis-ci.org/jindulys/GithubPilot)
3 |
4 | This is a Swift Github API Wrapper, it could make your life a little easier if you want to make an App with Github's wonderful data.
5 |
6 | # Installation
7 |
8 | ## CocoaPods
9 |
10 | Add a `Podfile` to your project, then edit it by adding:
11 |
12 | use_frameworks!
13 | pod 'GithubPilot', '~>1.0.3'
14 |
15 | then, run the following command:
16 |
17 | $ pod install
18 |
19 | From now on you should use `{Project}.xcworkspace` to open your project
20 |
21 | # Before You start
22 |
23 | ## Setup Your developer applications
24 |
25 | Go to your Github homepage, tap your avatar -> Setting, on your left choose **Applications** -> **Developer applications**, then you should tap **register a new OAuth application** on your top right side.
26 |
27 | Remember you should use a custom **Authorization callback URL**, which will be used later, eg. FunnyGithubTest://random
28 | After registration, you could get your **Client ID** and **Client Secret**.
29 |
30 | ## Setup Your Project
31 |
32 | To allow your user to be re-directed back to your app after OAuth dance, you'll need to associate a custom URL scheme with your app.
33 |
34 | Open your Xcode then open **Info.plist** of your project. copy and paste following code to your Info.plist source code.
35 |
36 | CFBundleURLTypes
37 |
38 |
39 | CFBundleURLSchemes
40 |
41 | your.custom.scheme(eg. FunnyGithubTest)
42 |
43 |
44 |
45 |
46 | # Usage
47 |
48 | ## Authentication
49 | First, add `import GithubPilot` at the top of your **AppDelegate**. You could then add `application(_: didFinishLaunchingWithOptions:)` with following to authenticate your client. You also should take care of `scope` parameter that your client will use, refer to [Github Scope](https://developer.github.com/v3/oauth/#scopes)
50 |
51 | Github.setupClientID("YourClientID", clientSecret: "YourClientSecret", scope: ["user", "repo"], redirectURI: "YourCustomCallBackURL")
52 | Github.authenticate()
53 |
54 | Second, add following code to your **AppDelegate** to get Github _**access token**_
55 |
56 | func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool
57 | {
58 | Github.requestAccessToken(url)
59 | return true
60 | }
61 |
62 | ## Used in Code
63 |
64 | ### Users
65 |
66 | #### Get the authenticated user
67 |
68 | if let client = Github.authorizedClient {
69 | client.users.getAuthenticatedUser().response({ user, requestError in
70 | if let me = user {
71 | print(me.description)
72 | } else {
73 | print(requestError?.description)
74 | }
75 | })
76 | }
77 | #### Get a user with username
78 |
79 | if let client = Github.authorizedClient {
80 | client.users.getUser(username: "onevcat").response({ (githubUser, error) -> Void in
81 | if let user = githubUser {
82 | print(user.description)
83 | } else {
84 | print(error?.description)
85 | }
86 | })
87 | }
88 |
89 | #### Get a page of users from `since` id
90 |
91 | if let client = Github.authorizedClient {
92 | client.users.getAllUsers("1209").response({ (httpResponse, users, requestError) -> Void in
93 | if let response = httpResponse {
94 | // next `since` id
95 | print("Since :\(response)")
96 | }
97 | if let result = users {
98 | for user in result {
99 | print(user.description)
100 | }
101 | } else {
102 | print(requestError?.description)
103 | }
104 | })
105 | }
106 |
107 | ### Repositories
108 |
109 | #### Get repositories of authenticated user
110 |
111 | if let client = Github.authorizedClient {
112 | client.repos.getAuthenticatedUserRepos().response({ (result, error) -> Void in
113 | if let repos = result {
114 | print(repos.count)
115 | for i in repos {
116 | print(i.name)
117 | print(i.stargazersCount)
118 | }
119 | }
120 | if let requestError = error {
121 | print(requestError.description)
122 | }
123 | })
124 | }
125 |
126 | #### Get a repo by repo name and repo owner name
127 |
128 | if let client = Github.authorizedClient {
129 | client.repos.getRepo("Yep", owner: "CatchChat").response({ (result, error) -> Void in
130 | if let repo = result {
131 | print(repo.name)
132 | }
133 | if let requestError = error {
134 | print(requestError.description)
135 | }
136 | })
137 | }
138 |
139 | #### Get repos belong to a user
140 |
141 | if let client = Github.authorizedClient {
142 | client.repos.getRepoFrom(owner: "onevcat").response({ (nextPage, result, error) -> Void in
143 | if let page = nextPage {
144 | print("Next Page is \(page)")
145 | }
146 | if let repos = result {
147 | print(repos.count)
148 | for r in repos {
149 | print(r.name)
150 | print(r.stargazersCount)
151 | }
152 | }
153 | if let requestError = error {
154 | print(requestError.description)
155 | }
156 | })
157 | }
158 |
159 | ### Events
160 |
161 | #### Get received events for a user
162 |
163 | if let client = Github.authorizedClient {
164 | client.events.getReceivedEventsForUser("someUser", page: "1").response({ (nextpage, results, error) -> Void in
165 | if let events = results {
166 | // New events
167 | }
168 | })
169 | }
170 |
171 | # Example
172 |
173 | You could refer to one of my project [GitPocket](https://github.com/jindulys/GitPocket) as an example.
174 |
175 | # Credits
176 |
177 | [SwiftyDropbox](https://github.com/dropbox/SwiftyDropbox)
178 |
179 | # Future Work
180 |
181 | There all tons of other API I haven't implementated, like **Search**. I will continuously make this repo better. Welcome to pull request and open issues.
182 |
--------------------------------------------------------------------------------
/Sources/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-19.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | * Constants used by GithubPilot SDK.
13 | */
14 | public struct Constants {
15 | /**
16 | * Notification Key
17 | */
18 | public struct NotificationKey {
19 | public static let GithubAccessTokenRequestSuccess = "GithubAccessTokenRequestSuccess"
20 | public static let GithubAccessTokenRequestFailure = "GithubAccessTokenRequestFailure"
21 | }
22 |
23 | public struct AccessToken {
24 | public static let GithubAccessTokenStorageKey = "GithubAccessTokenStorageKey"
25 | }
26 |
27 | /**
28 | ErrorInfo for GithubPilot
29 |
30 | - RequestOverTime: as the name is
31 | - InvalidInput: this means your input value is invalid, e.g empty inputs.
32 | - InvalidOperation: this means the order of your function call is incorrect, check log info.
33 | */
34 | public enum ErrorInfo: String {
35 | case RequestOverTime = "GithubPilot Request Over Time"
36 | case InvalidInput = "GithubPilot Invalid Input"
37 | case InvalidOperation = "GithubPilot Invalid Call Order"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/Github.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Github.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-19.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Convenience Class
12 | open class Github {
13 | /// authorizedClient used for the whole app to interact with Github API.
14 | open static var authorizedClient: GithubClient?
15 | /// authenticatedUser is the user who authenticates this client.
16 | open static var authenticatedUser: GithubUser?
17 |
18 | /**
19 | This method should be called at the very first to setup related Object.
20 |
21 | - parameter clientID: your clientID registered at Github Developer Page.
22 | - parameter clientSecret: your clientSecret registered at Github Developer Page.
23 | - parameter scope: scopes you want your client to have.
24 | - parameter redirectURI: unique URL that your client could deal with.
25 | */
26 | open static func setupClientID(_ clientID: String, clientSecret: String, scope:[String], redirectURI: String) {
27 | if GithubAuthManager.sharedAuthManager != nil {
28 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Only call `Github.setupClientID` once")
29 | }
30 | GithubAuthManager.sharedAuthManager = GithubAuthManager(clientID: clientID, clientSecret: clientSecret, scope: scope, redirectURI: redirectURI)
31 |
32 | // Call `sharedManager` once, to create this singleton.
33 | _ = GithubManager.sharedManager
34 | }
35 |
36 | /**
37 | Authenticate this client, should be called after `setupClientID(_, clientSecret:,scope:,redirectURI:)`
38 | */
39 | open static func authenticate() {
40 | if GithubAuthManager.sharedAuthManager == nil {
41 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Call `Github.setupClientID` before this method")
42 | }
43 | GithubAuthManager.sharedAuthManager.authenticate()
44 | }
45 |
46 | /**
47 | Request AccessToken.
48 |
49 | - parameter url: url returned by Authentication Server, this usually should be called from `application(_, openURL:,sourceApplication:,annotation)`
50 | */
51 | open static func requestAccessToken(_ url: URL) {
52 | if GithubAuthManager.sharedAuthManager == nil {
53 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Call `Github.setupClientID` before this method")
54 | }
55 |
56 | GithubAuthManager.sharedAuthManager.requestAccessToken(url)
57 | }
58 |
59 | /**
60 | Unlink this app.
61 | */
62 | open static func unlink() {
63 | if GithubAuthManager.sharedAuthManager == nil {
64 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Call `Github.setupClientID` before this method")
65 | }
66 |
67 | if Github.authorizedClient == nil {
68 | return
69 | }
70 |
71 | GithubAuthManager.sharedAuthManager.clearStoredAccessToken()
72 | Github.authorizedClient = nil
73 | }
74 | }
75 |
76 | /// Object used for monitor Notification
77 | class GithubManager: NSObject {
78 | static let sharedManager = GithubManager()
79 |
80 | fileprivate override init() {
81 | super.init()
82 | NotificationCenter.default.addObserver(self, selector:#selector(GithubManager.receivedGithubAccessToken), name: NSNotification.Name(rawValue: Constants.NotificationKey.GithubAccessTokenRequestSuccess), object: nil)
83 | NotificationCenter.default.addObserver(self, selector: #selector(GithubManager.receivedGithubAccessTokenFailure), name: NSNotification.Name(rawValue: Constants.NotificationKey.GithubAccessTokenRequestFailure), object: nil)
84 | }
85 |
86 | /**
87 | Get called when get AccessToken.
88 | */
89 | func receivedGithubAccessToken() {
90 | if GithubAuthManager.sharedAuthManager == nil {
91 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Call `Github.setupClientID` before this method")
92 | }
93 |
94 | if Github.authorizedClient != nil {
95 | print(Constants.ErrorInfo.InvalidOperation.rawValue + "Client has already been authorized")
96 | }
97 |
98 | if let accessToken = GithubAuthManager.sharedAuthManager.accessToken {
99 | Github.authorizedClient = GithubClient(accessToken: accessToken)
100 | Github.authorizedClient?.users.getAuthenticatedUser().response({ (result, error) -> Void in
101 | if let user = result {
102 | Github.authenticatedUser = user
103 | }
104 | // TODO: what if we could not get authenticated user, does this matter a lot?
105 | })
106 | }
107 | }
108 |
109 | /**
110 | Getting AccessToken Failed.
111 | */
112 | func receivedGithubAccessTokenFailure() {
113 | // TODO: save the error to display to the user
114 | print("Failed to get access token")
115 | }
116 |
117 | deinit {
118 | NotificationCenter.default.removeObserver(self)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Sources/GithubClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GithubClient.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-19.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | /// Class used for all kinds of request, it manages all the routes.
13 | open class GithubClient: GithubNetWorkClient {
14 | let accessToken: String
15 |
16 | open var users: UsersRoutes!
17 | open var repos: ReposRoutes!
18 | open var events: EventsRoutes!
19 | open var stars: StarsRoutes!
20 | open var searchRepo: GithubSearchRepoRoutes!
21 |
22 | /**
23 | Add additionalHeaders if you want.
24 |
25 | - parameter needoauth: need add accessToken to header.
26 |
27 | - returns: modified header.
28 | */
29 | open override func additionalHeaders(_ needoauth: Bool) -> [String : String] {
30 | var headers: [String: String] = [:]
31 | if needoauth {
32 | headers["Authorization"] = "token \(accessToken)"
33 | }
34 | return headers
35 | }
36 |
37 | /**
38 | Convenience Initializer
39 |
40 | - parameter accessToken: take an access token to initialize
41 |
42 | - returns: a client that takes charge of all kinds of API Request.
43 | */
44 | public convenience init(accessToken: String) {
45 | let configuration = URLSessionConfiguration.default
46 | configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
47 | let manager = Alamofire.SessionManager(configuration: configuration)
48 | manager.startRequestsImmediately = false
49 | self.init(accessToken: accessToken,
50 | manager: manager,
51 | baseHosts: ["api": "https://api.github.com"])
52 | }
53 |
54 | init(accessToken: String, manager: Alamofire.SessionManager, baseHosts: [String: String]) {
55 | self.accessToken = accessToken
56 | super.init(manager: manager, baseHosts: baseHosts)
57 | self.users = UsersRoutes(client: self)
58 | self.repos = ReposRoutes(client: self)
59 | self.events = EventsRoutes(client: self)
60 | self.stars = StarsRoutes(client: self)
61 | self.searchRepo = GithubSearchRepoRoutes(client: self)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/Helpers/Character+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSCharacter+Extensions.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-02-21.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | Extension: Character
13 | */
14 | public extension Character {
15 | /**
16 | Convert a Character to unicodeScalar value
17 | e.g turn 'a' to 97
18 | */
19 | func unicodeScalarCodePoint() -> Int {
20 | let characterString = String(self)
21 | let scalars = characterString.unicodeScalars
22 |
23 | return Int(scalars[scalars.startIndex].value)
24 | }
25 |
26 | /**
27 | Convert a Character to unicodeScalar value based on `0`
28 | e.g turn '0' to 0
29 | */
30 | func zeroCharacterBasedunicodeScalarCodePoint() -> Int {
31 | return self.unicodeScalarCodePoint() - 48
32 | }
33 | }
--------------------------------------------------------------------------------
/Sources/Helpers/Dictionray+Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Dictionray+Extensions.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-03-24.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | * Protocol for generate query string
13 | */
14 | public protocol QueryStringGenerator {
15 | /**
16 | protocol func that use any source to generate a query string, this is an abstraction
17 |
18 | - parameter source: source to generate a query string with.
19 | */
20 | func generateQueryStringWithSource(_ source: Any) -> String
21 | }
22 |
23 | // MARK: - GithubPilot Dictionary Extension
24 | public extension Dictionary {
25 |
26 | /**
27 | Generate a query string from `self` with generator
28 |
29 | - parameter generator: generator that could use dictionary to generate a query String
30 | */
31 | public func queryStringWithGenerator(_ generator: QueryStringGenerator) -> String {
32 | return generator.generateQueryStringWithSource(self)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/Helpers/Helper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helper.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-03-25.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /**
12 | Special URL query escape for `GithubSearch`, this one will not escape `+`, otherwise search could not wokr.
13 |
14 | - parameter string: The string to be percent-escaped.
15 |
16 | - returns: The percent-escaped string.
17 | */
18 | func githubSearchURLQueryEscape(_ string: String) -> String {
19 | let generalDelimitersToEncode = ":#[]@"
20 |
21 | let subDelimitersToEncode = "!$&'()*,;="
22 |
23 | let allowedCharacterSet = (CharacterSet.urlQueryAllowed as NSCharacterSet).mutableCopy() as! NSMutableCharacterSet
24 | allowedCharacterSet.removeCharacters(in: generalDelimitersToEncode + subDelimitersToEncode)
25 |
26 | var escaped = ""
27 | if #available(iOS 10.0, OSX 10.12, *) {
28 | escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet as CharacterSet) ?? string
29 | } else {
30 | let batchSize = 50
31 | var index = string.startIndex
32 |
33 | while index != string.endIndex {
34 | let startIndex = index
35 | var endIndex: String.Index
36 | if let batchedEndIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) {
37 | endIndex = batchedEndIndex
38 | } else {
39 | endIndex = string.endIndex
40 | }
41 | let range = (startIndex ..< endIndex)
42 | let substring = string.substring(with: range)
43 | escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet as CharacterSet) ?? substring
44 | index = endIndex
45 | }
46 | }
47 | return escaped
48 | }
49 |
50 | /**
51 | Creates percent-escaped, URL encoded query string components from the given key-value pair.
52 | */
53 | func githubSearchQueryComponents(_ key: String, _ value: String) -> [(String, String)] {
54 | var components: [(String, String)] = []
55 |
56 | components.append((githubSearchURLQueryEscape(key), githubSearchURLQueryEscape(value)))
57 |
58 | return components
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/Models/GithubEvent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GithubEvent.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-02-21.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /// GithubEventType https://developer.github.com/v3/activity/events/types/
12 | public enum EventType: String {
13 | case CreateEvent = "CreateEvent"
14 | case CommitCommentEvent = "CommitCommmentEvent"
15 | case DeleteEvent = "DeleteEvent"
16 | case DeploymentEvent = "DeploymentEvent"
17 | case DeploymentStatusEvent = "DeploymentStatusEvent"
18 | case DownloadEvent = "DownloadEvent"
19 | case FollowEvent = "FollowEvent"
20 | case ForkEvent = "ForkEvent"
21 | case ForkApplyEvent = "ForkApplyEvent"
22 | case GistEvent = "GistEvent"
23 | case GollumEvent = "GollumEvent"
24 | case IssueCommentEvent = "IssueCommentEvent"
25 | case IssuesEvent = "IssuesEvent"
26 | case MemberEvent = "MemberEvent"
27 | case MembershipEvent = "MembershipEvent"
28 | case PageBuildEvent = "PageBuildEvent"
29 | case PublicEvent = "PublicEvent"
30 | case PullRequestEvent = "PullRequestEvent"
31 | case PullRequestReviewCommentEvent = "PullRequestReviewCommentEvent"
32 | case PushEvent = "PushEvent"
33 | case ReleaseEvent = "ReleaseEvent"
34 | case RepositoryEvent = "RepositoryEvent"
35 | case StatusEvent = "StatusEvent"
36 | case TeamAddEvent = "TeamAddEvent"
37 | case WatchEvent = "WatchEvent"
38 |
39 | init?(event:String) {
40 | switch event {
41 | case "CreateEvent":
42 | self = .CreateEvent
43 | case "CommitCommentEvent":
44 | self = .CommitCommentEvent
45 | case "DeleteEvent":
46 | self = .DeleteEvent
47 | case "DeploymentEvent":
48 | self = .DeploymentEvent
49 | case "DeploymentStatusEvent":
50 | self = .DeploymentStatusEvent
51 | case "DownloadEvent":
52 | self = .DownloadEvent
53 | case "FollowEvent":
54 | self = .FollowEvent
55 | case "ForkEvent":
56 | self = .ForkEvent
57 | case "ForkApplyEvent":
58 | self = .ForkApplyEvent
59 | case "GistEvent":
60 | self = .GistEvent
61 | case "GollumEvent":
62 | self = .GollumEvent
63 | case "IssueCommentEvent":
64 | self = .IssueCommentEvent
65 | case "IssuesEvent":
66 | self = .IssuesEvent
67 | case "MemberEvent":
68 | self = .MemberEvent
69 | case "MembershipEvent":
70 | self = .MembershipEvent
71 | case "PageBuildEvent":
72 | self = .PageBuildEvent
73 | case "PublicEvent":
74 | self = .PublicEvent
75 | case "PullRequestEvent":
76 | self = .PullRequestEvent
77 | case "PullRequestReviewCommentEvent":
78 | self = .PullRequestReviewCommentEvent
79 | case "PushEvent":
80 | self = .PushEvent
81 | case "ReleaseEvent":
82 | self = .ReleaseEvent
83 | case "RepositoryEvent":
84 | self = .RepositoryEvent
85 | case "StatusEvent":
86 | self = .StatusEvent
87 | case "TeamAddEvent":
88 | self = .TeamAddEvent
89 | case "WatchEvent":
90 | self = .WatchEvent
91 | default:
92 | print("There has \(event)")
93 | return nil
94 | }
95 | }
96 | }
97 |
98 | /// GithubEvent represents a Github Event
99 | open class GithubEvent {
100 | open let id: String
101 | open let type: EventType
102 | open let repo: GithubRepo?
103 | open let actor: GithubUser?
104 | open let createdAt: String
105 |
106 | init(id: String, type: EventType, repo: GithubRepo? = nil, actor: GithubUser? = nil, createdAt: String) {
107 | self.id = id
108 | self.type = type
109 | self.repo = repo
110 | self.actor = actor
111 | self.createdAt = createdAt
112 | }
113 | }
114 |
115 | /// EventSerializer GithubEvent <---> JSON
116 | open class EventSerializer: JSONSerializer {
117 | let userSerializer: GithubUserSerializer
118 | let repoSerializer: RepoSerializer
119 |
120 | public init() {
121 | self.userSerializer = GithubUserSerializer()
122 | self.repoSerializer = RepoSerializer()
123 | }
124 |
125 | /**
126 | GithubEvent -> JSON
127 | */
128 | open func serialize(_ value: GithubEvent) -> JSON {
129 | let retVal = [
130 | "id": Serialization._StringSerializer.serialize(value.id),
131 | "type": Serialization._StringSerializer.serialize(value.type.rawValue),
132 | "repo": NullableSerializer(self.repoSerializer).serialize(value.repo),
133 | "actor": NullableSerializer(self.userSerializer).serialize(value.actor),
134 | "created_at": Serialization._StringSerializer.serialize(value.createdAt)
135 | ]
136 | return .dictionary(retVal)
137 | }
138 |
139 | /**
140 | JSON -> GithubEvent
141 | */
142 | open func deserialize(_ json: JSON) -> GithubEvent {
143 | switch json {
144 | case .dictionary(let dict):
145 | let id = Serialization._StringSerializer.deserialize(dict["id"] ?? .null)
146 | let type = EventType(event: Serialization._StringSerializer.deserialize(dict["type"] ?? .null))!
147 | let repo = NullableSerializer(self.repoSerializer).deserialize(dict["repo"] ?? .null)
148 | let actor = NullableSerializer(self.userSerializer).deserialize(dict["actor"] ?? .null)
149 | let createdAt = Serialization._StringSerializer.deserialize(dict["created_at"] ?? .null)
150 | return GithubEvent(id: id, type: type, repo: repo, actor: actor, createdAt: createdAt)
151 | default:
152 | fatalError("Github Event JSON Type Error")
153 | }
154 | }
155 | }
156 |
157 | /// Event Array Serializer, which deal with array.
158 | open class EventArraySerializer: JSONSerializer {
159 | let eventSerializer: EventSerializer
160 | init() {
161 | self.eventSerializer = EventSerializer()
162 | }
163 |
164 | /**
165 | [GithubEvent] -> JSON
166 | */
167 | open func serialize(_ value: [GithubEvent]) -> JSON {
168 | let users = value.map { self.eventSerializer.serialize($0) }
169 | return .array(users)
170 | }
171 |
172 | /**
173 | JSON -> [GithubEvent]
174 | */
175 | open func deserialize(_ json: JSON) -> [GithubEvent] {
176 | switch json {
177 | case .array(let users):
178 | return users.map { self.eventSerializer.deserialize($0) }
179 | default:
180 | fatalError("JSON Type should be array")
181 | }
182 | }
183 | }
184 |
185 |
--------------------------------------------------------------------------------
/Sources/OAuth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OAuth.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-19.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | /**
13 | GithubAuthResult Enum
14 |
15 | - Success: Success
16 | - Error: Error
17 | */
18 | public enum GithubAuthResult {
19 | case success(String)
20 | case error(String)
21 | }
22 |
23 | /// GithubAuthManager
24 | open class GithubAuthManager {
25 | let clientID: String
26 | let clientSecret: String
27 | let redirectURI: String
28 | let oAuthRouter: GithubAuthenticationRoutes
29 | let oAuthClient: GithubNetWorkClient
30 | let scope: [String]
31 |
32 | var code: String?
33 | var accessToken: String?
34 | var oAuthResult: GithubAuthResult?
35 |
36 | open static var sharedAuthManager: GithubAuthManager!
37 |
38 | init(clientID: String, clientSecret: String, scope:[String], redirectURI: String) {
39 | self.clientID = clientID
40 | self.clientSecret = clientSecret
41 | self.redirectURI = redirectURI
42 | self.scope = scope
43 | let configuration = URLSessionConfiguration.default
44 | configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
45 | let manager = Alamofire.SessionManager(configuration:configuration)
46 | manager.startRequestsImmediately = false
47 | self.oAuthClient = GithubNetWorkClient(manager: manager,
48 | baseHosts: [
49 | "login": "https://github.com",
50 | "api": "https://api.github.com"])
51 | self.oAuthRouter = GithubAuthenticationRoutes(client: self.oAuthClient)
52 | }
53 |
54 | /**
55 | Authenticate client with Github authrization server. This is the first step of
56 | Github OAuth procedure.
57 | */
58 | open func authenticate() {
59 | if let accessToken = DefaultStorage.get(key: Constants.AccessToken.GithubAccessTokenStorageKey) as? String {
60 | self.accessToken = accessToken
61 | self.oAuthResult = .success(accessToken)
62 | NotificationCenter.default.post(name: Notification.Name(rawValue: Constants.NotificationKey.GithubAccessTokenRequestSuccess), object: nil)
63 | } else {
64 | self.oAuthRouter.requestAuthentication(self.scope, clientID: self.clientID, redirectURI: self.redirectURI)
65 | }
66 | }
67 |
68 | /**
69 | Request AccessToken
70 |
71 | - parameter url: url with `code` value, got from Authentication Step.
72 | */
73 | open func requestAccessToken(_ url: URL) {
74 | guard let code = url.query?.components(separatedBy: "code=").last else { return }
75 | self.oAuthRouter.requestAccessToken(self.clientID, clientSecret: self.clientSecret, code: code) { (tokenString, requestError) -> Void in
76 | if let error = requestError {
77 | self.oAuthResult = .error(error.description)
78 | NotificationCenter.default.post(name: Notification.Name(rawValue: Constants.NotificationKey.GithubAccessTokenRequestFailure), object: nil)
79 | return
80 | }
81 | if let result = tokenString {
82 | let components = result.components(separatedBy: "&")
83 | componentLoop: for component in components {
84 | let items = component.components(separatedBy: "=")
85 | var isToken = false
86 | itemLoop: for item in items {
87 | if isToken {
88 | self.accessToken = item
89 | self.oAuthResult = .success(item)
90 | // Clear storaged access token
91 | DefaultStorage.clear(key: Constants.AccessToken.GithubAccessTokenStorageKey)
92 | // Save access token
93 | DefaultStorage.save(item as AnyObject,
94 | withKey: Constants.AccessToken.GithubAccessTokenStorageKey)
95 | break componentLoop
96 | }
97 |
98 | if item == "access_token" {
99 | isToken = true
100 | }
101 | }
102 | }
103 | NotificationCenter.default.post(name: Notification.Name(rawValue: Constants.NotificationKey.GithubAccessTokenRequestSuccess), object: nil)
104 | }
105 | }
106 | }
107 |
108 | /**
109 | Clear AccessToken.
110 | */
111 | open func clearStoredAccessToken() {
112 | DefaultStorage.clear(key: Constants.AccessToken.GithubAccessTokenStorageKey)
113 | }
114 | }
115 |
116 |
117 | /**
118 | * Protocol for PersistentStorage
119 | */
120 | protocol PersistentStorage {
121 | associatedtype valueType
122 | associatedtype keyType
123 |
124 | static func save(_ info: valueType, withKey key: keyType) -> Void
125 | static func get(key: keyType) -> AnyObject?
126 | static func clear(key: keyType) -> Void
127 | }
128 |
129 | /// DefaultStorage use NSDefault to save information
130 | class DefaultStorage: PersistentStorage {
131 | /**
132 | Save info with key to UserDefaults
133 |
134 | - parameter info: info to be saved.
135 | - parameter key: key.
136 | */
137 | class func save(_ info: AnyObject, withKey key: String) -> Void {
138 | let defaults = UserDefaults.standard
139 | defaults.set(info, forKey: key)
140 | }
141 |
142 | /**
143 | Get info for a key from UserDefaults
144 |
145 | - parameter key: key
146 |
147 | - returns: related value or nil.
148 | */
149 | class func get(key: String) -> AnyObject? {
150 | let defaults = UserDefaults.standard
151 | if let value = defaults.object(forKey: key) {
152 | return value as AnyObject?
153 | }
154 | return nil
155 | }
156 |
157 | /**
158 | Remove one key from UserDefaults
159 |
160 | - parameter key: key to be cleaned.
161 | */
162 | class func clear(key: String) {
163 | let defaults = UserDefaults.standard
164 | defaults.removeObject(forKey: key)
165 | }
166 | }
167 |
168 |
--------------------------------------------------------------------------------
/Sources/Routes/AuthenticationRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuthenticationRoutes.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-18.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | /// A Simple Class Represent one Authentication Information
13 | struct GithubAuthentication {
14 | let id: Int32
15 | let token: String
16 | let hashedToken: String
17 | }
18 |
19 | /// Serializer used for Authentication
20 | class GithubAuthenticationSerializer: JSONSerializer {
21 | init() { }
22 |
23 | func serialize(_ value: GithubAuthentication) -> JSON {
24 | var retVal:[String: JSON] = [:]
25 | retVal["id"] = Serialization._Int32Serializer.serialize(value.id)
26 | retVal["token"] = Serialization._StringSerializer.serialize(value.token)
27 | retVal["hashed_token"] = Serialization._StringSerializer.serialize(value.hashedToken)
28 | return .dictionary(retVal)
29 | }
30 |
31 | func deserialize(_ json: JSON) -> GithubAuthentication {
32 | switch json {
33 | case .dictionary(let dict):
34 | let id = Serialization._Int32Serializer.deserialize(dict["id"] ?? .null)
35 | let token = Serialization._StringSerializer.deserialize(dict["token"] ?? .null)
36 | let hashedToken = Serialization._StringSerializer.deserialize(dict["hashed_token"] ?? .null)
37 | return GithubAuthentication(id: id, token: token, hashedToken: hashedToken)
38 | default:
39 | fatalError("Wrong Type")
40 | }
41 | }
42 | }
43 |
44 | enum AuthorizationError: CustomStringConvertible {
45 | case invalidParameter
46 | case httpError(String)
47 | case unknownResponse(String)
48 |
49 | var description: String {
50 | switch self {
51 | case .invalidParameter:
52 | return "Invalid Parameter please check your parameter format"
53 | case .httpError(let error):
54 | return "HTTP Error :\(error)"
55 | case .unknownResponse(let error):
56 | return "Unknown response :\(error)"
57 | }
58 | }
59 | }
60 |
61 | /// Router used for Authentication purpose.
62 |
63 | /// This Router is different from other router since the response for this one is not a JSON Format, although we could change this behavior by set HTTP Header fields.
64 | class GithubAuthenticationRoutes {
65 | unowned let client: GithubNetWorkClient
66 | init(client: GithubNetWorkClient) {
67 | self.client = client
68 | }
69 |
70 | /**
71 | Request Authentication
72 |
73 | - parameter scopes: scopes used by your client app
74 | - parameter clientID: clientID
75 | - parameter redirectURI: redirectURI should be a unique scheme that your application could deal with.
76 | */
77 | func requestAuthentication(_ scopes:[String], clientID: String, redirectURI: String) {
78 | guard let login = self.client.baseHosts["login"] else { return }
79 | let path = "/login/oauth/authorize"
80 | // TODO: optimize params generate process, use a cooler way.
81 | let urlString = "\(login)\(path)?client_id=\(clientID)&redirect_uri=\(redirectURI)&scope=\(scopes.joined(separator: ","))"
82 | if let url = URL(string: urlString) {
83 | UIApplication.shared.openURL(url)
84 | } else {
85 | fatalError("Client should have login URL")
86 | }
87 | }
88 |
89 | // TODO: deal with error, might be create error ENUM for authentication
90 | /**
91 | Request to access Token
92 |
93 | - parameter clientID: ClientID, required
94 | - parameter clientSecret: ClientSecret, required
95 | - parameter code: the code you get from authentication
96 | - parameter complitionHandler: complitionHandler will return a string contains access_token, or an Error
97 | */
98 | func requestAccessToken(_ clientID: String,
99 | clientSecret: String,
100 | code: String,
101 | complitionHandler: @escaping (String?, AuthorizationError?) ->Void) {
102 | let url = "\(self.client.baseHosts["login"]!)/login/oauth/access_token"
103 | let accessTokenRequest =
104 | "client_id=\(clientID)&client_secret=\(clientSecret)&code=\(code)"
105 | guard let postData = accessTokenRequest.data(using: String.Encoding.ascii, allowLossyConversion: true) else {
106 | return complitionHandler(nil,.invalidParameter)
107 | }
108 |
109 | Alamofire.request(url, method: .post,
110 | parameters: ["":""],
111 | encoding: DataPostEncoding(data: postData),
112 | headers: nil).response { response in
113 | let d = response.data!
114 | if let error = response.error, let response = response.response {
115 | complitionHandler(nil, .httpError("Request Error, code: \(response.statusCode) description:\(error.localizedDescription)"))
116 | } else {
117 | if let tokenResponse = NSString(data:d, encoding: String.Encoding.ascii.rawValue) as? String {
118 | complitionHandler(tokenResponse, nil)
119 | } else {
120 | complitionHandler(nil,.unknownResponse("could not decode response data"))
121 | }
122 | }
123 | }
124 |
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/Sources/Routes/EventsRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EventsRoutes.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-02-21.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /// Routes responsible for Events related request.
12 | open class EventsRoutes {
13 | open unowned let client: GithubNetWorkClient
14 |
15 | init(client: GithubNetWorkClient) {
16 | self.client = client
17 | }
18 |
19 | /**
20 | Events that a user has received
21 |
22 | - parameter name: user
23 | - parameter page: when user has a lot of repos, pagination will be applied.
24 |
25 | - returns: an RpcRequest, whose response result contains `[GithubEvent]`, if pagination is applicable, response result contains `nextpage`.
26 | */
27 | open func getReceivedEventsForUser(_ name: String, page: String = "1") -> RpcCustomResponseRequest {
28 | if name.characters.count == 0 {
29 | print(Constants.ErrorInfo.InvalidInput.rawValue)
30 | }
31 | return RpcCustomResponseRequest(client: self.client,
32 | host: "api",
33 | route: "/users/\(name)/received_events",
34 | method: .get,
35 | params: ["page":page],
36 | postParams: nil,
37 | postData: nil,
38 | customResponseHandler:GPHttpResponseHandler.PageHandler,
39 | responseSerializer: EventArraySerializer(),
40 | errorSerializer: StringSerializer())
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Routes/ReposRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ReposRoutes.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-02-21.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | open class ReposRoutes {
12 | open unowned let client: GithubNetWorkClient
13 | init(client: GithubNetWorkClient) {
14 | self.client = client
15 | }
16 |
17 | /**
18 | Get current authenticated user's repo.
19 |
20 | - returns: an RpcRequest, whose response result contains `[GithubRepo]`.
21 | */
22 | open func getAuthenticatedUserRepos() -> RpcRequest {
23 | return RpcRequest(client: self.client,
24 | host: "api",
25 | route: "/user/repos",
26 | method: .get,
27 | responseSerializer: RepoArraySerializer(),
28 | errorSerializer: StringSerializer())
29 | }
30 |
31 | /**
32 | Get a specific repo with repo name and repo owner name
33 |
34 | - parameter name: this repo's name
35 | - parameter owner: repo's owner name
36 |
37 | - returns: an RpcRequest, whose response result contain `GithubRepo`.
38 | */
39 | open func getRepo(_ name: String, owner: String) -> RpcRequest {
40 | if name.characters.count == 0 || owner.characters.count == 0 {
41 | print(Constants.ErrorInfo.InvalidInput.rawValue)
42 | }
43 |
44 | return RpcRequest(client: self.client,
45 | host: "api",
46 | route: "/repos/\(owner)/\(name)",
47 | method: .get,
48 | responseSerializer: RepoSerializer(),
49 | errorSerializer: StringSerializer())
50 | }
51 |
52 | /**
53 | List public repositories for the specified user.
54 |
55 | - parameter owner: user name
56 | - parameter page: when user has a lot of repos, pagination will be applied.
57 |
58 | - returns: an RpcRequest, whose response result contains `[GithubRepo]`, if pagination is applicable, response result contains `nextpage`.
59 |
60 | - note: Note that page numbering is 1-based and that omitting the ?page parameter will return the first page.
61 | */
62 | open func getRepoFrom(owner: String, page: String = "1") -> RpcCustomResponseRequest {
63 | if owner.characters.count == 0 {
64 | print(Constants.ErrorInfo.InvalidInput.rawValue)
65 | }
66 |
67 | return RpcCustomResponseRequest(client: self.client,
68 | host: "api",
69 | route: "/users/\(owner)/repos",
70 | method: .get,
71 | params: ["page":page],
72 | postParams: nil,
73 | postData: nil,
74 | customResponseHandler:GPHttpResponseHandler.PageHandler,
75 | responseSerializer: RepoArraySerializer(),
76 | errorSerializer: StringSerializer())
77 | }
78 |
79 | /**
80 | Use API URL to get the full information of Repo
81 |
82 | - parameter url: api URL
83 |
84 | - returns: Request Object you could get the full information from its successful serializer.
85 | */
86 | open func getAPIRepo(url: String) -> DirectAPIRequest {
87 | if url.characters.count == 0 {
88 | print(Constants.ErrorInfo.InvalidInput.rawValue)
89 | }
90 |
91 | return DirectAPIRequest(client: self.client,
92 | apiURL: url,
93 | method: .get,
94 | responseSerializer: RepoSerializer(),
95 | errorSerializer: StringSerializer())
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Sources/Routes/SearchRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchRoutes.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-03-23.
6 | //
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 |
13 | /**
14 | Enumeration for Github Search Query Parameters
15 | */
16 | public enum GitHubSearchCondition: String {
17 | // Comm Search Condition
18 | case Within = "in"
19 | case Size = "size"
20 | case Fork = "fork"
21 | case User = "user"
22 | case Repo = "repo"
23 | case Language = "language"
24 |
25 | // Sepecific for Search Repo
26 | case Forks = "forks"
27 | case Created = "created"
28 | case Pushed = "pushed"
29 | case Stars = "stars"
30 |
31 | // Sepecific for Search Code
32 | case Extension = "extension"
33 | case FileName = "filename"
34 | case Path = "path"
35 |
36 | // Sepecific for Search Users
37 | case `Type` = "type"
38 | case Repos = "repos"
39 | case Location = "location"
40 | case Followers = "followers"
41 | }
42 |
43 | /**
44 | * Github Search Specific Query Generator
45 | */
46 | struct GithubSearchQueryGenerator: QueryStringGenerator {
47 |
48 | /**
49 | GithubSearch Query Generator
50 |
51 | - parameter source: dictionary with `GithubSearchCondition: String`
52 |
53 | - returns: format like `language:Swift+repo:leetcode`
54 | */
55 | func generateQueryStringWithSource(_ source: Any) -> String {
56 | var retVal = ""
57 |
58 | if let conditionDic = source as? [GitHubSearchCondition: String] {
59 | var queryPair: [String] = []
60 | for (condition, value) in conditionDic {
61 | let pair = condition.rawValue + ":" + value
62 | queryPair.append(pair)
63 | }
64 |
65 | retVal = queryPair.joined(separator: "+")
66 | }
67 |
68 | return retVal
69 | }
70 | }
71 |
72 | internal struct URLQueryEncoding: ParameterEncoding {
73 | func query(_ parameters: [String: String]) -> String {
74 | var components: [(String, String)] = []
75 |
76 | for key in parameters.keys.sorted(by: <) {
77 | let value = parameters[key]!
78 | components += githubSearchQueryComponents(key, value)
79 | }
80 |
81 | return (components.map { "\($0)=\($1)" } as [String]).joined(separator: "&")
82 | }
83 |
84 | func encode(_ urlRequest: URLRequestConvertible,
85 | with parameters: Parameters?) throws -> URLRequest {
86 | guard var urlRequest = urlRequest.urlRequest else {
87 | throw GithubRequestError.InvalidRequest
88 | }
89 | if let URLComponents = NSURLComponents(url: urlRequest.url!, resolvingAgainstBaseURL: false),
90 | let validParams = parameters as? [String: String] {
91 | let percentEncodedQuery = (URLComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(validParams)
92 | URLComponents.percentEncodedQuery = percentEncodedQuery
93 | urlRequest.url = URLComponents.url
94 | }
95 | return urlRequest
96 | }
97 | }
98 |
99 | /// Github Search Repo Routes
100 | open class GithubSearchRepoRoutes {
101 |
102 | /**
103 | The sort field. One of stars, forks, or updated. Default: results are sorted by best match.
104 | */
105 | public enum SearchRepoSort: String {
106 | case Stars = "stars"
107 | case Forks = "forks"
108 | case Updated = "updated"
109 | }
110 |
111 | /**
112 | The sort order if sort parameter is provided. One of asc or desc. Default: desc
113 | */
114 | public enum SearchRepoOrder: String {
115 | case Asc = "asc"
116 | case Desc = "desc"
117 | }
118 |
119 | open unowned let client: GithubNetWorkClient
120 | init(client: GithubNetWorkClient) {
121 | self.client = client
122 | }
123 |
124 | /**
125 | Search Repo for a topic
126 |
127 | - parameter topic: topic to search
128 | - parameter sort: sort type
129 | - parameter order: order type
130 | - parameter conditionDict: additional search condition [GitHubSearchCondition: String]
131 | - parameter page: page info
132 |
133 | - returns: rpc request
134 | */
135 | open func searchRepoForTopic(_ topic: String,
136 | sort: SearchRepoSort = .Updated,
137 | order: SearchRepoOrder = .Desc,
138 | conditionDict: [GitHubSearchCondition: String]? = nil,
139 | page: String = "1") -> RpcCustomResponseRequest {
140 | if topic.characters.count == 0 {
141 | print(Constants.ErrorInfo.InvalidInput.rawValue)
142 | }
143 | var topicQuery = topic
144 | if let conditions = conditionDict {
145 | topicQuery += "+" + conditions.queryStringWithGenerator(GithubSearchQueryGenerator())
146 | }
147 |
148 |
149 | return RpcCustomResponseRequest(client: self.client,
150 | host: "api",
151 | route: "/search/repositories",
152 | method: .get,
153 | params: ["q":topicQuery, "sort": sort.rawValue, "order": order.rawValue, "page":page],
154 | postParams: nil,
155 | postData: nil,
156 | encoding: URLQueryEncoding(),
157 | customResponseHandler: GPHttpResponseHandler.PageHandler,
158 | responseSerializer: SearchResultSerializer(),
159 | errorSerializer: StringSerializer())
160 | }
161 | }
162 |
163 | /// RepoArraySerializer
164 | open class SearchResultSerializer: JSONSerializer {
165 | let reposSerializer: RepoArraySerializer
166 | init() {
167 | self.reposSerializer = RepoArraySerializer()
168 | }
169 |
170 | /**
171 | descriptions
172 | */
173 | open func serialize(_ value: [GithubRepo]) -> JSON {
174 | return .null
175 | }
176 |
177 | /**
178 | JSON -> [GithubRepo]
179 | */
180 | open func deserialize(_ json: JSON) -> [GithubRepo] {
181 | switch json {
182 | case .dictionary(let infoDict):
183 | var retVal: [GithubRepo] = []
184 | if let items = infoDict["items"] {
185 | retVal = self.reposSerializer.deserialize(items)
186 | }
187 |
188 | return retVal
189 | default:
190 | fatalError("JSON Type should be array")
191 | }
192 | }
193 | }
194 |
195 |
196 |
197 |
--------------------------------------------------------------------------------
/Sources/Routes/StarsRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StarsRoutes.swift
3 | // Pods
4 | //
5 | // Created by yansong li on 2016-02-22.
6 | //
7 | //
8 |
9 | import Foundation
10 |
11 | /// Routes for Stars related request.
12 | open class StarsRoutes {
13 | open unowned let client: GithubNetWorkClient
14 | // This queue is used for some long time task to stay around, espicially pagination operation.
15 | let longTimeWaitQueue: DispatchQueue
16 |
17 | init(client: GithubNetWorkClient) {
18 | self.client = client
19 | self.longTimeWaitQueue = DispatchQueue(label: "com.githubpilot.stargazersRoutes.waitingQueue", attributes: [])
20 | }
21 |
22 | /**
23 | Users that stars a repo belongs to a user.
24 |
25 | - parameter repo: repo name
26 | - parameter name: owner
27 | - parameter page: when user has a lot of repos, pagination will be applied.
28 |
29 | - returns: an RpcRequest, whose response result contains `[GithubUser]`, if pagination is applicable, response result contains `nextpage`.
30 | */
31 | open func getStargazersFor(repo: String,
32 | owner: String,
33 | page: String = "1",
34 | defaultResponseQueue: DispatchQueue? = nil) -> RpcCustomResponseRequest {
35 | if repo.characters.count == 0 || owner.characters.count == 0 {
36 | print("Repo name and Owner name must not be empty")
37 | }
38 | return RpcCustomResponseRequest(client: self.client,
39 | host: "api",
40 | route: "/repos/\(owner)/\(repo)/stargazers",
41 | method: .get,
42 | params: ["page":page],
43 | postParams: nil,
44 | postData: nil,
45 | customResponseHandler: GPHttpResponseHandler.PageHandler,
46 | defaultResponseQueue: defaultResponseQueue,
47 | responseSerializer: UserArraySerializer(),
48 | errorSerializer: StringSerializer())
49 | }
50 |
51 | /**
52 | Get all the stargazers belong to a owner's repo.
53 |
54 | - note: This request is time consuming if this repo is a quite popular one. but it will run on a private serial queue and will not block main queue.
55 |
56 | - parameter repo: repo's name.
57 | - parameter owner: owner's name.
58 | - parameter complitionHandler: callback that call on main thread.
59 | */
60 | fileprivate func getAllStargazersOldFor(repo: String,
61 | owner: String,
62 | complitionHandler:@escaping ([GithubUser]?, String?)-> Void) {
63 | self.longTimeWaitQueue.async { () -> Void in
64 | let privateQueue = DispatchQueue(label: "com.githubpilot.stargazersRoutes.responseQueue", attributes: [])
65 | var retVal: [GithubUser] = []
66 | var retError: String?
67 | let semaphore = DispatchSemaphore(value: 0)
68 | var recursiveStargazers: (String, String, String, DispatchQueue?) -> Void = {_, _, _, _ in }
69 | recursiveStargazers = {
70 | repo, owner, page, queue in
71 | self.getStargazersFor(repo: repo,
72 | owner: owner,
73 | page: page,
74 | defaultResponseQueue: queue).response {
75 | (nextPage, result, error) -> Void in
76 | if let error = error {
77 | retError = error.description
78 | semaphore.signal()
79 | }
80 |
81 | if let users = result {
82 | retVal.append(contentsOf: users)
83 | }
84 |
85 | if let vpage = nextPage {
86 | if vpage == "1" {
87 | semaphore.signal()
88 | } else {
89 | recursiveStargazers(repo, owner, vpage, queue)
90 | }
91 | }
92 | }
93 | }
94 |
95 | recursiveStargazers(repo, owner, "1", privateQueue)
96 | let timeoutTime = DispatchTime.now() + Double(Int64(100 * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
97 | if semaphore.wait(timeout: timeoutTime) == .timedOut {
98 | retError = Constants.ErrorInfo.RequestOverTime.rawValue
99 | }
100 | DispatchQueue.main.async(execute: { () -> Void in
101 | complitionHandler(retVal, retError)
102 | })
103 | }
104 | }
105 |
106 | /**
107 | Get all the stargazers belong to a owner's repo.
108 |
109 | - note: This request is time consuming if this repo is a quite popular one. but it will run on a private serial queue and will not block main queue.
110 |
111 | - parameter repo: repo's name.
112 | - parameter owner: owner's name.
113 | - parameter complitionHandler: callback that call on main thread.
114 | */
115 | open func getAllStargazersFor(repo: String,
116 | owner: String,
117 | complitionHandler: @escaping ([GithubUser]?, String?) -> Void) {
118 | var recursiveStargazers: (String, String, String) -> Void = {_, _, _ in }
119 | var retVal: [GithubUser] = []
120 | recursiveStargazers = {
121 | repo, owner, page in
122 | self.getStargazersFor(repo: repo, owner: owner, page: page).response {
123 | (nextPage, result, error) -> Void in
124 | guard let users = result, let vpage = nextPage else {
125 | complitionHandler(nil, error?.description ?? "Error,Could not finish this request")
126 | return
127 | }
128 |
129 | retVal.append(contentsOf: users)
130 | if vpage == "1" {
131 | complitionHandler(retVal, nil)
132 | } else {
133 | recursiveStargazers(repo, owner, vpage)
134 | }
135 | }
136 | }
137 | recursiveStargazers(repo, owner, "1")
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Sources/Routes/UsersRoutes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserRoutes.swift
3 | // GitPocket
4 | //
5 | // Created by yansong li on 2016-02-17.
6 | // Copyright © 2016 yansong li. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Routes for User related Request.
12 | open class UsersRoutes {
13 | open unowned let client: GithubNetWorkClient
14 | init(client: GithubNetWorkClient) {
15 | self.client = client
16 | }
17 |
18 | /**
19 | Get a single user.
20 |
21 | - parameter username: a Github user's username.
22 |
23 | - returns: an RpcRequest, whose response result is `GithubUser`.
24 | */
25 | open func getUser(username: String) -> RpcRequest {
26 | return RpcRequest(client: self.client,
27 | host: "api",
28 | route: "/users/\(username)",
29 | method: .get,
30 | responseSerializer: GithubUserSerializer(),
31 | errorSerializer: StringSerializer())
32 | }
33 |
34 | /**
35 | Get followers for user.
36 |
37 | - parameter user: a Github user's username.
38 |
39 | - parameter page: a specific page to query.
40 |
41 | - returns: an RpcRequest, where contains an array of followers.
42 | */
43 | open func getFollowersFor(user: String, page: String = "1") -> RpcCustomResponseRequest {
44 | return RpcCustomResponseRequest(client: self.client,
45 | host: "api",
46 | route: "/users/\(user)/followers",
47 | method: .get,
48 | params: ["page" : page],
49 | postParams: nil,
50 | postData: nil,
51 | customResponseHandler: GPHttpResponseHandler.PageHandler,
52 | responseSerializer: UserArraySerializer(),
53 | errorSerializer: StringSerializer())
54 |
55 | }
56 |
57 | /**
58 | Get following for user.
59 |
60 | - parameter user: a Github user's username.
61 |
62 | - parameter page: a specific page to query.
63 |
64 | - returns: an RpcRequest, where contains an array of followers.
65 | */
66 | open func getFollowingFor(user: String, page: String = "1") -> RpcCustomResponseRequest {
67 | return RpcCustomResponseRequest(client: self.client,
68 | host: "api",
69 | route: "/users/\(user)/following",
70 | method: .get,
71 | params: ["page" : page],
72 | postParams: nil,
73 | postData: nil,
74 | customResponseHandler: GPHttpResponseHandler.PageHandler,
75 | responseSerializer: UserArraySerializer(),
76 | errorSerializer: StringSerializer())
77 | }
78 |
79 | /**
80 | Get current authenticated user.
81 |
82 | - returns: an RpcRequest, whose response result is `GithubUser`.
83 | */
84 | open func getAuthenticatedUser() -> RpcRequest {
85 | return RpcRequest(client: self.client,
86 | host: "api",
87 | route: "/user",
88 | method: .get,
89 | responseSerializer: GithubUserSerializer(),
90 | errorSerializer: StringSerializer())
91 | }
92 |
93 | /**
94 | Get all Users, this is a pagination request, you could get `since` which represent next page of users' start ID from `.response` complitionHandler's first paramete
95 |
96 | - parameter since: The integer ID of the last User that you've seen, you could get this from `.response` complitionHandler's first parameter
97 |
98 | - returns: an RpcCustomResponseRequest
99 | */
100 | open func getAllUsers(_ since: String) -> RpcCustomResponseRequest {
101 | if since.characters.count == 0 {
102 | print(Constants.ErrorInfo.InvalidInput.rawValue)
103 | }
104 | let params = ["since": since]
105 | return RpcCustomResponseRequest(client: self.client,
106 | host: "api",
107 | route: "/users",
108 | method: .get,
109 | params: params,
110 | postParams: nil,
111 | postData: nil,
112 | customResponseHandler: GPHttpResponseHandler.SinceHandler,
113 | responseSerializer: UserArraySerializer(),
114 | errorSerializer: StringSerializer())
115 | }
116 |
117 | /**
118 | Get a user's full information through API url.
119 |
120 | - parameter url: user's api url, e.g `https://api.github.com/users/octocat`.
121 |
122 | - returns: a DirectAPIRequest, which you could use to get user's info through `response` method.
123 | */
124 | open func getAPIUser(url: String) -> DirectAPIRequest {
125 | if url.characters.count == 0 {
126 | print("GithubPilotError Invalid input")
127 | }
128 | return DirectAPIRequest(client: self.client,
129 | apiURL: url,
130 | method: .get,
131 | responseSerializer: GithubUserSerializer(),
132 | errorSerializer: StringSerializer())
133 | }
134 |
135 | /**
136 | Get a list of users' full information
137 |
138 | - parameter userAPIURLs: a list of url contains userAPIURL, which could be used to fetch for the full information
139 | - parameter complitionHandler: callback when all users get fetched, contains full information of users.
140 | */
141 | open func getFullUsers(_ userAPIURLs: [String], complitionHandler:@escaping ([GithubUser]?)->Void) {
142 | let fetchUserGroup = DispatchGroup()
143 | var results: [GithubUser] = []
144 | for url in userAPIURLs {
145 | if url.characters.count > 0 {
146 | // Enter group
147 | fetchUserGroup.enter()
148 | getAPIUser(url: url).response({ (result, error) -> Void in
149 | if let fetchError = error {
150 | print("Meeet an error \(fetchError)")
151 | fetchUserGroup.leave()
152 | }
153 |
154 | if let user = result {
155 | results.append(user)
156 | fetchUserGroup.leave()
157 | }
158 | })
159 | }
160 | }
161 |
162 | fetchUserGroup.notify(queue: DispatchQueue.main) { () -> Void in
163 | complitionHandler(results)
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------