├── .gitmodules
├── LICENSE
├── README.md
├── client
├── .gitignore
├── Ares.entitlements
├── Ares.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
├── AresKit
│ ├── AccessToken.swift
│ ├── AresKit.h
│ ├── Client.swift
│ ├── ConnectionManager.swift
│ ├── CreatedUser.swift
│ ├── CredentialStorage.swift
│ ├── DeviceName.swift
│ ├── FileTransferContext.swift
│ ├── IncomingFileTransfer.swift
│ ├── Info.plist
│ ├── JSONDeserializable.swift
│ ├── OutgoingFileTransfer.swift
│ ├── PushNotification.swift
│ ├── RegisteredDevice.swift
│ └── User.swift
├── Mac
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── green_orb.imageset
│ │ │ ├── Contents.json
│ │ │ ├── green_orb.png
│ │ │ └── green_orb@2x.png
│ │ ├── red_orb.imageset
│ │ │ ├── Contents.json
│ │ │ ├── red_orb.png
│ │ │ └── red_orb@2x.png
│ │ ├── rocket.imageset
│ │ │ ├── 777.png
│ │ │ └── Contents.json
│ │ └── yellow_orb.imageset
│ │ │ ├── Contents.json
│ │ │ ├── yellow_orb.png
│ │ │ └── yellow_orb@2x.png
│ ├── Base.lproj
│ │ └── MainMenu.xib
│ ├── Info.plist
│ ├── LoginWindowController.swift
│ ├── LoginWindowController.xib
│ └── StatusItemController.swift
└── iOS
│ ├── APNSManager.swift
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── placeholder.imageset
│ │ ├── Contents.json
│ │ ├── placeholder.png
│ │ ├── placeholder@2x.png
│ │ └── placeholder@3x.png
│ └── rocket.imageset
│ │ ├── 777.png
│ │ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── Info.plist
│ ├── LoginViewController.swift
│ ├── LoginViewController.xib
│ ├── NSDataExtensions.swift
│ ├── PreviewViewController.swift
│ ├── UIAlertControllerExtensions.swift
│ ├── ViewController.swift
│ └── ViewController.xib
├── server
├── .gitignore
├── Procfile
├── index.js
└── package.json
└── steps.png
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "client/External/Alamofire"]
2 | path = client/External/Alamofire
3 | url = https://github.com/Alamofire/Alamofire.git
4 | [submodule "client/External/KeychainAccess"]
5 | path = client/External/KeychainAccess
6 | url = git@github.com:kishikawakatsumi/KeychainAccess.git
7 | [submodule "client/External/KVOController"]
8 | path = client/External/KVOController
9 | url = git@github.com:facebook/KVOController.git
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Indragie Karunaratne
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚀 Ares
2 | **Zero-setup* P2P file transfer between Macs and iOS devices**
3 |
4 | Ares is a service that I built in under 24 hours, winning first place at [HackED 2016](http://eceweek.compeclub.com/hackathon/) during the University of Alberta's ECE Week.
5 |
6 | [**Presentation Slides**](https://speakerdeck.com/indragiek/ares-at-hacked-2016)
7 |
8 | **⚠️ Ares is a proof-of-concept tech demo. It is neither secure nor bug-free enough to be used in production. ⚠️**
9 |
10 | * Zero-setup refers to the lack of setup when sending or receiving a file (e.g. opening the AirDrop tab in a Finder window, opening an email client, browsing for files in Dropbox for iOS, etc.), *not* the initial process of installing and setting up the Ares apps
11 |
12 | ### Motivation
13 |
14 | The existing options for trasferring a file from a Mac to an iOS device (or vice versa) are clumsy or simply unreliable. I Commonly used methods like emailing a file to yourself or using Dropbox are inconvenient because the data has to be uploaded to an intermediary before it can be received by the target device. Apple's own AirDrop is often very unreliable, and the UX is less than ideal. Why do I have to open AirDrop in a Finder tab and *wait* for the devices to discover each other, which sometimes doesn't happen, in order to start a file transfer?
15 |
16 | Ares is a technology demo that shows how much more streamlined the entire process could be.
17 |
18 | ### Using Ares
19 |
20 |
21 |
22 |
23 |
24 | ### Setting Up
25 |
26 | #### Back-end
27 |
28 | I will not be providing a hosted service that can be used directly at this time due to security concerns. You are free to set up your own instance of the back-end, which is a [Node.js](https://nodejs.org/en/) application that was built to be deployed using [Heroku](https://heroku.com) and [MongoLab](https://mongolab.com/). Follow these steps to deploy your own Ares server instance:
29 |
30 | ##### Heroku
31 |
32 | 1. Fork the repository and clone it
33 | 2. [Set up APNS](https://github.com/dkhamsing/apns-guide), copy the `key.pem` and `cert.pem` files (named exactly that way) to the `/server` directory, and commit them
34 | 3. With the [Heroku Toolbelt](https://toolbelt.heroku.com/) installed, run the following commands from the root directory of the repository:
35 |
36 | ```
37 | $ heroku login
38 | $ heroku create
39 | $ heroku addons:create mongolab:sandbox
40 | $ heroku config:set APP_SECRET=$(uuidgen)
41 | $ git subtree push --prefix server heroku master
42 | ```
43 |
44 | ##### Running Locally
45 |
46 | ```
47 | $ cd server
48 | $ npm install
49 | $ export MONGOLAB_URI=$(heroku config:get MONGOLAB_URI)
50 | $ export APP_SECRET=$(heroku config:get APP_SECRET)
51 | $ node index.js
52 | ```
53 |
54 | Replace the `MONGOLAB_URI` and `APP_SECRET` definitions with your own values if you did not deploy the application on Heroku.
55 |
56 | #### iOS and Mac apps
57 |
58 | Both the iOS and Mac application targets are set up in `client/Ares.xcodeproj`. The iOS-specific code is in the `client/iOS` directory, and the Mac-specific code is in `client/Mac`. `client/AresKit` contains the source for `AresKit`, a cross platform framework containing all of the code shared between the iOS and Mac clients.
59 |
60 | Before building and running the apps, the `DefaultAPIURL` in `client/AresKit/Client.swift` will need to be changed to point to the URL for your Heroku instance, which can be obtained by running `heroku apps:info`.
61 |
62 | ### Future Ideas
63 |
64 | - Bidirectional file transfer: iOS → Mac transfers in addition to the Mac → iOS transfer implemented currently
65 | - A full file manager in the iOS app that lets you view and catalogue files that were previously downloaded over Ares
66 | - Multiple simultaneous file transfers
67 | - Better security in verifying origins and the integrity of delivered payloads
68 | - Fallback to uploading files to a storage server when P2P communication is not available
69 | - iOS action extension for sending content from 3rd party apps via Ares
70 |
71 | ### Contact
72 |
73 | * Indragie Karunaratne
74 | * [@indragie](http://twitter.com/indragie)
75 | * [http://indragie.com](http://indragie.com)
76 |
77 | ### License
78 |
79 | Ares is licensed under the MIT License. See `LICENSE` for more information.
80 |
--------------------------------------------------------------------------------
/client/.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 |
--------------------------------------------------------------------------------
/client/Ares.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | keychain-access-groups
6 |
7 | $(AppIdentifierPrefix)com.indragie.Ares-iOS
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/Ares.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 7203D1BE1C5D9F690011E3F0 /* APNSManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1BD1C5D9F690011E3F0 /* APNSManager.swift */; };
11 | 7203D1C01C5DA0CD0011E3F0 /* NSDataExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1BF1C5DA0CD0011E3F0 /* NSDataExtensions.swift */; };
12 | 7203D1C31C5DAD580011E3F0 /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1C21C5DAD580011E3F0 /* ConnectionManager.swift */; };
13 | 7203D1C41C5DAD580011E3F0 /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1C21C5DAD580011E3F0 /* ConnectionManager.swift */; };
14 | 7203D1C61C5DBA450011E3F0 /* StatusItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1C51C5DBA450011E3F0 /* StatusItemController.swift */; };
15 | 7203D1CA1C5DE20F0011E3F0 /* DeviceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1C91C5DE20F0011E3F0 /* DeviceName.swift */; };
16 | 7203D1CB1C5DE20F0011E3F0 /* DeviceName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7203D1C91C5DE20F0011E3F0 /* DeviceName.swift */; };
17 | 721C793F1C5D41D1008EFF59 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C793C1C5D41D1008EFF59 /* AppDelegate.swift */; };
18 | 721C79401C5D41D1008EFF59 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 721C793D1C5D41D1008EFF59 /* Assets.xcassets */; };
19 | 721C79441C5D41DD008EFF59 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 721C79421C5D41DD008EFF59 /* MainMenu.xib */; };
20 | 721C79751C5D428E008EFF59 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79711C5D428E008EFF59 /* AppDelegate.swift */; };
21 | 721C79761C5D428E008EFF59 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 721C79721C5D428E008EFF59 /* Assets.xcassets */; };
22 | 721C79781C5D428E008EFF59 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79741C5D428E008EFF59 /* ViewController.swift */; };
23 | 721C797D1C5D4294008EFF59 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 721C79791C5D4294008EFF59 /* LaunchScreen.storyboard */; };
24 | 721C79871C5D42DE008EFF59 /* AresKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 721C79861C5D42DE008EFF59 /* AresKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
25 | 721C798B1C5D42DE008EFF59 /* AresKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79841C5D42DE008EFF59 /* AresKit.framework */; };
26 | 721C798C1C5D42DE008EFF59 /* AresKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79841C5D42DE008EFF59 /* AresKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | 721C799D1C5D4301008EFF59 /* AresKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79961C5D4301008EFF59 /* AresKit.framework */; };
28 | 721C799E1C5D4301008EFF59 /* AresKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79961C5D4301008EFF59 /* AresKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
29 | 721C79A31C5D4316008EFF59 /* AresKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 721C79861C5D42DE008EFF59 /* AresKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
30 | 721C79A61C5D62A3008EFF59 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79A51C5D62A3008EFF59 /* Client.swift */; };
31 | 721C79A71C5D62A3008EFF59 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79A51C5D62A3008EFF59 /* Client.swift */; };
32 | 721C79C11C5D63B0008EFF59 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B81C5D63A1008EFF59 /* Alamofire.framework */; };
33 | 721C79C21C5D63B6008EFF59 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B41C5D63A1008EFF59 /* Alamofire.framework */; };
34 | 721C79C31C5D63BF008EFF59 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B81C5D63A1008EFF59 /* Alamofire.framework */; };
35 | 721C79C41C5D63BF008EFF59 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B81C5D63A1008EFF59 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
36 | 721C79C71C5D63C6008EFF59 /* Alamofire.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B41C5D63A1008EFF59 /* Alamofire.framework */; };
37 | 721C79C81C5D63C6008EFF59 /* Alamofire.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 721C79B41C5D63A1008EFF59 /* Alamofire.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
38 | 721C79CC1C5D642F008EFF59 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79CB1C5D642F008EFF59 /* User.swift */; };
39 | 721C79CD1C5D642F008EFF59 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79CB1C5D642F008EFF59 /* User.swift */; };
40 | 721C79CF1C5D64F2008EFF59 /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79CE1C5D64F2008EFF59 /* JSONDeserializable.swift */; };
41 | 721C79D01C5D64F2008EFF59 /* JSONDeserializable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79CE1C5D64F2008EFF59 /* JSONDeserializable.swift */; };
42 | 721C79D21C5D6514008EFF59 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79D11C5D6514008EFF59 /* AccessToken.swift */; };
43 | 721C79D31C5D6514008EFF59 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79D11C5D6514008EFF59 /* AccessToken.swift */; };
44 | 721C79D51C5D7276008EFF59 /* CreatedUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79D41C5D7276008EFF59 /* CreatedUser.swift */; };
45 | 721C79D61C5D7276008EFF59 /* CreatedUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79D41C5D7276008EFF59 /* CreatedUser.swift */; };
46 | 721C79DC1C5D78B7008EFF59 /* CredentialStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79DB1C5D78B7008EFF59 /* CredentialStorage.swift */; };
47 | 721C79DD1C5D78B7008EFF59 /* CredentialStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79DB1C5D78B7008EFF59 /* CredentialStorage.swift */; };
48 | 721C79F31C5D7969008EFF59 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79F21C5D7969008EFF59 /* Keychain.swift */; };
49 | 721C79F41C5D7969008EFF59 /* Keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79F21C5D7969008EFF59 /* Keychain.swift */; };
50 | 721C79F71C5D8404008EFF59 /* LoginWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79F51C5D8404008EFF59 /* LoginWindowController.swift */; };
51 | 721C79F81C5D8404008EFF59 /* LoginWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 721C79F61C5D8404008EFF59 /* LoginWindowController.xib */; };
52 | 721C79FB1C5D843E008EFF59 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79F91C5D843E008EFF59 /* LoginViewController.swift */; };
53 | 721C79FC1C5D843E008EFF59 /* LoginViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 721C79FA1C5D843E008EFF59 /* LoginViewController.xib */; };
54 | 721C79FE1C5D89A5008EFF59 /* UIAlertControllerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79FD1C5D89A5008EFF59 /* UIAlertControllerExtensions.swift */; };
55 | 721C7A001C5D9505008EFF59 /* RegisteredDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79FF1C5D9505008EFF59 /* RegisteredDevice.swift */; };
56 | 721C7A011C5D9505008EFF59 /* RegisteredDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 721C79FF1C5D9505008EFF59 /* RegisteredDevice.swift */; };
57 | 7252DD691C5DF12C00E681E1 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD681C5DF12C00E681E1 /* PushNotification.swift */; };
58 | 7252DD6A1C5DF12C00E681E1 /* PushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD681C5DF12C00E681E1 /* PushNotification.swift */; };
59 | 7252DD711C5DFA8B00E681E1 /* IncomingFileTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD701C5DFA8B00E681E1 /* IncomingFileTransfer.swift */; };
60 | 7252DD721C5DFA8B00E681E1 /* IncomingFileTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD701C5DFA8B00E681E1 /* IncomingFileTransfer.swift */; };
61 | 7252DD741C5DFC5D00E681E1 /* FileTransferContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD731C5DFC5D00E681E1 /* FileTransferContext.swift */; };
62 | 7252DD751C5DFC5D00E681E1 /* FileTransferContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD731C5DFC5D00E681E1 /* FileTransferContext.swift */; };
63 | 7252DD771C5DFD4400E681E1 /* OutgoingFileTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD761C5DFD4400E681E1 /* OutgoingFileTransfer.swift */; };
64 | 7252DD781C5DFD4400E681E1 /* OutgoingFileTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD761C5DFD4400E681E1 /* OutgoingFileTransfer.swift */; };
65 | 7252DD7A1C5E0F5A00E681E1 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7252DD791C5E0F5A00E681E1 /* ViewController.xib */; };
66 | 7252DD841C5E127A00E681E1 /* KVOController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7252DD811C5E126D00E681E1 /* KVOController.framework */; };
67 | 7252DD851C5E127A00E681E1 /* KVOController.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7252DD811C5E126D00E681E1 /* KVOController.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
68 | 7252DD891C5E14AA00E681E1 /* PreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7252DD881C5E14AA00E681E1 /* PreviewViewController.swift */; };
69 | /* End PBXBuildFile section */
70 |
71 | /* Begin PBXContainerItemProxy section */
72 | 721C79891C5D42DE008EFF59 /* PBXContainerItemProxy */ = {
73 | isa = PBXContainerItemProxy;
74 | containerPortal = 721C79241C5D418C008EFF59 /* Project object */;
75 | proxyType = 1;
76 | remoteGlobalIDString = 721C79831C5D42DE008EFF59;
77 | remoteInfo = AresKit;
78 | };
79 | 721C799B1C5D4301008EFF59 /* PBXContainerItemProxy */ = {
80 | isa = PBXContainerItemProxy;
81 | containerPortal = 721C79241C5D418C008EFF59 /* Project object */;
82 | proxyType = 1;
83 | remoteGlobalIDString = 721C79951C5D4301008EFF59;
84 | remoteInfo = "AresKit-iOS";
85 | };
86 | 721C79B31C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
87 | isa = PBXContainerItemProxy;
88 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
89 | proxyType = 2;
90 | remoteGlobalIDString = F8111E3319A95C8B0040E7D1;
91 | remoteInfo = "Alamofire iOS";
92 | };
93 | 721C79B51C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
94 | isa = PBXContainerItemProxy;
95 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
96 | proxyType = 2;
97 | remoteGlobalIDString = F8111E3E19A95C8B0040E7D1;
98 | remoteInfo = "Alamofire iOS Tests";
99 | };
100 | 721C79B71C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
101 | isa = PBXContainerItemProxy;
102 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
103 | proxyType = 2;
104 | remoteGlobalIDString = 4DD67C0B1A5C55C900ED2280;
105 | remoteInfo = "Alamofire OSX";
106 | };
107 | 721C79B91C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
108 | isa = PBXContainerItemProxy;
109 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
110 | proxyType = 2;
111 | remoteGlobalIDString = F829C6B21A7A94F100A2CD59;
112 | remoteInfo = "Alamofire OSX Tests";
113 | };
114 | 721C79BB1C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
115 | isa = PBXContainerItemProxy;
116 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
117 | proxyType = 2;
118 | remoteGlobalIDString = 4CF626EF1BA7CB3E0011A099;
119 | remoteInfo = "Alamofire tvOS";
120 | };
121 | 721C79BD1C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
122 | isa = PBXContainerItemProxy;
123 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
124 | proxyType = 2;
125 | remoteGlobalIDString = 4CF626F81BA7CB3E0011A099;
126 | remoteInfo = "Alamofire tvOS Tests";
127 | };
128 | 721C79BF1C5D63A1008EFF59 /* PBXContainerItemProxy */ = {
129 | isa = PBXContainerItemProxy;
130 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
131 | proxyType = 2;
132 | remoteGlobalIDString = E4202FE01B667AA100C997FB;
133 | remoteInfo = "Alamofire watchOS";
134 | };
135 | 721C79C51C5D63BF008EFF59 /* PBXContainerItemProxy */ = {
136 | isa = PBXContainerItemProxy;
137 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
138 | proxyType = 1;
139 | remoteGlobalIDString = 4DD67C0A1A5C55C900ED2280;
140 | remoteInfo = "Alamofire OSX";
141 | };
142 | 721C79C91C5D63C6008EFF59 /* PBXContainerItemProxy */ = {
143 | isa = PBXContainerItemProxy;
144 | containerPortal = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
145 | proxyType = 1;
146 | remoteGlobalIDString = F8111E3219A95C8B0040E7D1;
147 | remoteInfo = "Alamofire iOS";
148 | };
149 | 7252DD801C5E126D00E681E1 /* PBXContainerItemProxy */ = {
150 | isa = PBXContainerItemProxy;
151 | containerPortal = 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */;
152 | proxyType = 2;
153 | remoteGlobalIDString = D719F2981B73962E0090C2FB;
154 | remoteInfo = "KVOController-iOS";
155 | };
156 | 7252DD821C5E126D00E681E1 /* PBXContainerItemProxy */ = {
157 | isa = PBXContainerItemProxy;
158 | containerPortal = 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */;
159 | proxyType = 2;
160 | remoteGlobalIDString = D719F2BD1B7396AF0090C2FB;
161 | remoteInfo = "KVOController-OSX";
162 | };
163 | 7252DD861C5E127A00E681E1 /* PBXContainerItemProxy */ = {
164 | isa = PBXContainerItemProxy;
165 | containerPortal = 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */;
166 | proxyType = 1;
167 | remoteGlobalIDString = D719F2971B73962E0090C2FB;
168 | remoteInfo = "KVOController-iOS";
169 | };
170 | /* End PBXContainerItemProxy section */
171 |
172 | /* Begin PBXCopyFilesBuildPhase section */
173 | 721C79901C5D42DE008EFF59 /* Embed Frameworks */ = {
174 | isa = PBXCopyFilesBuildPhase;
175 | buildActionMask = 2147483647;
176 | dstPath = "";
177 | dstSubfolderSpec = 10;
178 | files = (
179 | 721C798C1C5D42DE008EFF59 /* AresKit.framework in Embed Frameworks */,
180 | 721C79C41C5D63BF008EFF59 /* Alamofire.framework in Embed Frameworks */,
181 | );
182 | name = "Embed Frameworks";
183 | runOnlyForDeploymentPostprocessing = 0;
184 | };
185 | 721C79A21C5D4302008EFF59 /* Embed Frameworks */ = {
186 | isa = PBXCopyFilesBuildPhase;
187 | buildActionMask = 2147483647;
188 | dstPath = "";
189 | dstSubfolderSpec = 10;
190 | files = (
191 | 7252DD851C5E127A00E681E1 /* KVOController.framework in Embed Frameworks */,
192 | 721C799E1C5D4301008EFF59 /* AresKit.framework in Embed Frameworks */,
193 | 721C79C81C5D63C6008EFF59 /* Alamofire.framework in Embed Frameworks */,
194 | );
195 | name = "Embed Frameworks";
196 | runOnlyForDeploymentPostprocessing = 0;
197 | };
198 | /* End PBXCopyFilesBuildPhase section */
199 |
200 | /* Begin PBXFileReference section */
201 | 7203D1BD1C5D9F690011E3F0 /* APNSManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = APNSManager.swift; path = iOS/APNSManager.swift; sourceTree = SOURCE_ROOT; };
202 | 7203D1BF1C5DA0CD0011E3F0 /* NSDataExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NSDataExtensions.swift; path = iOS/NSDataExtensions.swift; sourceTree = SOURCE_ROOT; };
203 | 7203D1C11C5DA8F10011E3F0 /* Ares.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Ares.entitlements; sourceTree = ""; };
204 | 7203D1C21C5DAD580011E3F0 /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = ""; };
205 | 7203D1C51C5DBA450011E3F0 /* StatusItemController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = StatusItemController.swift; path = Mac/StatusItemController.swift; sourceTree = SOURCE_ROOT; };
206 | 7203D1C91C5DE20F0011E3F0 /* DeviceName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceName.swift; sourceTree = ""; };
207 | 721C792C1C5D418C008EFF59 /* Ares.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ares.app; sourceTree = BUILT_PRODUCTS_DIR; };
208 | 721C793C1C5D41D1008EFF59 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Mac/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
209 | 721C793D1C5D41D1008EFF59 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Mac/Assets.xcassets; sourceTree = SOURCE_ROOT; };
210 | 721C793E1C5D41D1008EFF59 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Mac/Info.plist; sourceTree = SOURCE_ROOT; };
211 | 721C79431C5D41DD008EFF59 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; };
212 | 721C795F1C5D426C008EFF59 /* Ares.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ares.app; sourceTree = BUILT_PRODUCTS_DIR; };
213 | 721C79711C5D428E008EFF59 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = iOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
214 | 721C79721C5D428E008EFF59 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = iOS/Assets.xcassets; sourceTree = SOURCE_ROOT; };
215 | 721C79731C5D428E008EFF59 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = SOURCE_ROOT; };
216 | 721C79741C5D428E008EFF59 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = iOS/ViewController.swift; sourceTree = SOURCE_ROOT; };
217 | 721C797A1C5D4294008EFF59 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = iOS/Base.lproj/LaunchScreen.storyboard; sourceTree = SOURCE_ROOT; };
218 | 721C79841C5D42DE008EFF59 /* AresKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AresKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
219 | 721C79861C5D42DE008EFF59 /* AresKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AresKit.h; sourceTree = ""; };
220 | 721C79881C5D42DE008EFF59 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
221 | 721C79961C5D4301008EFF59 /* AresKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AresKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
222 | 721C79A51C5D62A3008EFF59 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; };
223 | 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Alamofire.xcodeproj; path = External/Alamofire/Alamofire.xcodeproj; sourceTree = ""; };
224 | 721C79CB1C5D642F008EFF59 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; };
225 | 721C79CE1C5D64F2008EFF59 /* JSONDeserializable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONDeserializable.swift; sourceTree = ""; };
226 | 721C79D11C5D6514008EFF59 /* AccessToken.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = ""; };
227 | 721C79D41C5D7276008EFF59 /* CreatedUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreatedUser.swift; sourceTree = ""; };
228 | 721C79DB1C5D78B7008EFF59 /* CredentialStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialStorage.swift; sourceTree = ""; };
229 | 721C79F21C5D7969008EFF59 /* Keychain.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Keychain.swift; path = External/KeychainAccess/Lib/KeychainAccess/Keychain.swift; sourceTree = SOURCE_ROOT; };
230 | 721C79F51C5D8404008EFF59 /* LoginWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoginWindowController.swift; path = Mac/LoginWindowController.swift; sourceTree = SOURCE_ROOT; };
231 | 721C79F61C5D8404008EFF59 /* LoginWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = LoginWindowController.xib; path = Mac/LoginWindowController.xib; sourceTree = SOURCE_ROOT; };
232 | 721C79F91C5D843E008EFF59 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoginViewController.swift; path = iOS/LoginViewController.swift; sourceTree = SOURCE_ROOT; };
233 | 721C79FA1C5D843E008EFF59 /* LoginViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = LoginViewController.xib; path = iOS/LoginViewController.xib; sourceTree = SOURCE_ROOT; };
234 | 721C79FD1C5D89A5008EFF59 /* UIAlertControllerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtensions.swift; path = iOS/UIAlertControllerExtensions.swift; sourceTree = SOURCE_ROOT; };
235 | 721C79FF1C5D9505008EFF59 /* RegisteredDevice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegisteredDevice.swift; sourceTree = ""; };
236 | 7252DD681C5DF12C00E681E1 /* PushNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotification.swift; sourceTree = ""; };
237 | 7252DD701C5DFA8B00E681E1 /* IncomingFileTransfer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncomingFileTransfer.swift; sourceTree = ""; };
238 | 7252DD731C5DFC5D00E681E1 /* FileTransferContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileTransferContext.swift; sourceTree = ""; };
239 | 7252DD761C5DFD4400E681E1 /* OutgoingFileTransfer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingFileTransfer.swift; sourceTree = ""; };
240 | 7252DD791C5E0F5A00E681E1 /* ViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ViewController.xib; path = iOS/ViewController.xib; sourceTree = SOURCE_ROOT; };
241 | 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = DynamicFramework.xcodeproj; path = External/KVOController/DynamicFramework.xcodeproj; sourceTree = ""; };
242 | 7252DD881C5E14AA00E681E1 /* PreviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreviewViewController.swift; path = iOS/PreviewViewController.swift; sourceTree = SOURCE_ROOT; };
243 | /* End PBXFileReference section */
244 |
245 | /* Begin PBXFrameworksBuildPhase section */
246 | 721C79291C5D418C008EFF59 /* Frameworks */ = {
247 | isa = PBXFrameworksBuildPhase;
248 | buildActionMask = 2147483647;
249 | files = (
250 | 721C798B1C5D42DE008EFF59 /* AresKit.framework in Frameworks */,
251 | 721C79C31C5D63BF008EFF59 /* Alamofire.framework in Frameworks */,
252 | );
253 | runOnlyForDeploymentPostprocessing = 0;
254 | };
255 | 721C795C1C5D426C008EFF59 /* Frameworks */ = {
256 | isa = PBXFrameworksBuildPhase;
257 | buildActionMask = 2147483647;
258 | files = (
259 | 7252DD841C5E127A00E681E1 /* KVOController.framework in Frameworks */,
260 | 721C799D1C5D4301008EFF59 /* AresKit.framework in Frameworks */,
261 | 721C79C71C5D63C6008EFF59 /* Alamofire.framework in Frameworks */,
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | };
265 | 721C79801C5D42DE008EFF59 /* Frameworks */ = {
266 | isa = PBXFrameworksBuildPhase;
267 | buildActionMask = 2147483647;
268 | files = (
269 | 721C79C11C5D63B0008EFF59 /* Alamofire.framework in Frameworks */,
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | 721C79921C5D4301008EFF59 /* Frameworks */ = {
274 | isa = PBXFrameworksBuildPhase;
275 | buildActionMask = 2147483647;
276 | files = (
277 | 721C79C21C5D63B6008EFF59 /* Alamofire.framework in Frameworks */,
278 | );
279 | runOnlyForDeploymentPostprocessing = 0;
280 | };
281 | /* End PBXFrameworksBuildPhase section */
282 |
283 | /* Begin PBXGroup section */
284 | 721C79231C5D418C008EFF59 = {
285 | isa = PBXGroup;
286 | children = (
287 | 7203D1C11C5DA8F10011E3F0 /* Ares.entitlements */,
288 | 721C79A81C5D6397008EFF59 /* External */,
289 | 721C792E1C5D418C008EFF59 /* Mac */,
290 | 721C79601C5D426C008EFF59 /* iOS */,
291 | 721C79851C5D42DE008EFF59 /* AresKit */,
292 | 721C792D1C5D418C008EFF59 /* Products */,
293 | );
294 | sourceTree = "";
295 | };
296 | 721C792D1C5D418C008EFF59 /* Products */ = {
297 | isa = PBXGroup;
298 | children = (
299 | 721C792C1C5D418C008EFF59 /* Ares.app */,
300 | 721C795F1C5D426C008EFF59 /* Ares.app */,
301 | 721C79841C5D42DE008EFF59 /* AresKit.framework */,
302 | 721C79961C5D4301008EFF59 /* AresKit.framework */,
303 | );
304 | name = Products;
305 | sourceTree = "";
306 | };
307 | 721C792E1C5D418C008EFF59 /* Mac */ = {
308 | isa = PBXGroup;
309 | children = (
310 | 721C793C1C5D41D1008EFF59 /* AppDelegate.swift */,
311 | 721C793D1C5D41D1008EFF59 /* Assets.xcassets */,
312 | 721C793E1C5D41D1008EFF59 /* Info.plist */,
313 | 721C79421C5D41DD008EFF59 /* MainMenu.xib */,
314 | 721C79F51C5D8404008EFF59 /* LoginWindowController.swift */,
315 | 721C79F61C5D8404008EFF59 /* LoginWindowController.xib */,
316 | 7203D1C51C5DBA450011E3F0 /* StatusItemController.swift */,
317 | );
318 | name = Mac;
319 | path = Ares;
320 | sourceTree = "";
321 | };
322 | 721C79601C5D426C008EFF59 /* iOS */ = {
323 | isa = PBXGroup;
324 | children = (
325 | 721C79711C5D428E008EFF59 /* AppDelegate.swift */,
326 | 721C79721C5D428E008EFF59 /* Assets.xcassets */,
327 | 721C79731C5D428E008EFF59 /* Info.plist */,
328 | 721C79741C5D428E008EFF59 /* ViewController.swift */,
329 | 721C79791C5D4294008EFF59 /* LaunchScreen.storyboard */,
330 | 721C79F91C5D843E008EFF59 /* LoginViewController.swift */,
331 | 721C79FA1C5D843E008EFF59 /* LoginViewController.xib */,
332 | 721C79FD1C5D89A5008EFF59 /* UIAlertControllerExtensions.swift */,
333 | 7203D1BD1C5D9F690011E3F0 /* APNSManager.swift */,
334 | 7203D1BF1C5DA0CD0011E3F0 /* NSDataExtensions.swift */,
335 | 7252DD791C5E0F5A00E681E1 /* ViewController.xib */,
336 | 7252DD881C5E14AA00E681E1 /* PreviewViewController.swift */,
337 | );
338 | name = iOS;
339 | path = "Ares-iOS";
340 | sourceTree = "";
341 | };
342 | 721C79851C5D42DE008EFF59 /* AresKit */ = {
343 | isa = PBXGroup;
344 | children = (
345 | 721C79861C5D42DE008EFF59 /* AresKit.h */,
346 | 721C79881C5D42DE008EFF59 /* Info.plist */,
347 | 721C79A51C5D62A3008EFF59 /* Client.swift */,
348 | 721C79CB1C5D642F008EFF59 /* User.swift */,
349 | 721C79CE1C5D64F2008EFF59 /* JSONDeserializable.swift */,
350 | 721C79D11C5D6514008EFF59 /* AccessToken.swift */,
351 | 721C79D41C5D7276008EFF59 /* CreatedUser.swift */,
352 | 721C79DB1C5D78B7008EFF59 /* CredentialStorage.swift */,
353 | 721C79F21C5D7969008EFF59 /* Keychain.swift */,
354 | 721C79FF1C5D9505008EFF59 /* RegisteredDevice.swift */,
355 | 7203D1C21C5DAD580011E3F0 /* ConnectionManager.swift */,
356 | 7203D1C91C5DE20F0011E3F0 /* DeviceName.swift */,
357 | 7252DD681C5DF12C00E681E1 /* PushNotification.swift */,
358 | 7252DD701C5DFA8B00E681E1 /* IncomingFileTransfer.swift */,
359 | 7252DD761C5DFD4400E681E1 /* OutgoingFileTransfer.swift */,
360 | 7252DD731C5DFC5D00E681E1 /* FileTransferContext.swift */,
361 | );
362 | path = AresKit;
363 | sourceTree = "";
364 | };
365 | 721C79A81C5D6397008EFF59 /* External */ = {
366 | isa = PBXGroup;
367 | children = (
368 | 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */,
369 | 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */,
370 | );
371 | name = External;
372 | sourceTree = "";
373 | };
374 | 721C79AA1C5D63A1008EFF59 /* Products */ = {
375 | isa = PBXGroup;
376 | children = (
377 | 721C79B41C5D63A1008EFF59 /* Alamofire.framework */,
378 | 721C79B61C5D63A1008EFF59 /* Alamofire iOS Tests.xctest */,
379 | 721C79B81C5D63A1008EFF59 /* Alamofire.framework */,
380 | 721C79BA1C5D63A1008EFF59 /* Alamofire OSX Tests.xctest */,
381 | 721C79BC1C5D63A1008EFF59 /* Alamofire.framework */,
382 | 721C79BE1C5D63A1008EFF59 /* Alamofire tvOS Tests.xctest */,
383 | 721C79C01C5D63A1008EFF59 /* Alamofire.framework */,
384 | );
385 | name = Products;
386 | sourceTree = "";
387 | };
388 | 7252DD7C1C5E126D00E681E1 /* Products */ = {
389 | isa = PBXGroup;
390 | children = (
391 | 7252DD811C5E126D00E681E1 /* KVOController.framework */,
392 | 7252DD831C5E126D00E681E1 /* KVOController.framework */,
393 | );
394 | name = Products;
395 | sourceTree = "";
396 | };
397 | /* End PBXGroup section */
398 |
399 | /* Begin PBXHeadersBuildPhase section */
400 | 721C79811C5D42DE008EFF59 /* Headers */ = {
401 | isa = PBXHeadersBuildPhase;
402 | buildActionMask = 2147483647;
403 | files = (
404 | 721C79871C5D42DE008EFF59 /* AresKit.h in Headers */,
405 | );
406 | runOnlyForDeploymentPostprocessing = 0;
407 | };
408 | 721C79931C5D4301008EFF59 /* Headers */ = {
409 | isa = PBXHeadersBuildPhase;
410 | buildActionMask = 2147483647;
411 | files = (
412 | 721C79A31C5D4316008EFF59 /* AresKit.h in Headers */,
413 | );
414 | runOnlyForDeploymentPostprocessing = 0;
415 | };
416 | /* End PBXHeadersBuildPhase section */
417 |
418 | /* Begin PBXNativeTarget section */
419 | 721C792B1C5D418C008EFF59 /* Ares-Mac */ = {
420 | isa = PBXNativeTarget;
421 | buildConfigurationList = 721C79391C5D418C008EFF59 /* Build configuration list for PBXNativeTarget "Ares-Mac" */;
422 | buildPhases = (
423 | 721C79281C5D418C008EFF59 /* Sources */,
424 | 721C79291C5D418C008EFF59 /* Frameworks */,
425 | 721C792A1C5D418C008EFF59 /* Resources */,
426 | 721C79901C5D42DE008EFF59 /* Embed Frameworks */,
427 | );
428 | buildRules = (
429 | );
430 | dependencies = (
431 | 721C798A1C5D42DE008EFF59 /* PBXTargetDependency */,
432 | 721C79C61C5D63BF008EFF59 /* PBXTargetDependency */,
433 | );
434 | name = "Ares-Mac";
435 | productName = Ares;
436 | productReference = 721C792C1C5D418C008EFF59 /* Ares.app */;
437 | productType = "com.apple.product-type.application";
438 | };
439 | 721C795E1C5D426C008EFF59 /* Ares-iOS */ = {
440 | isa = PBXNativeTarget;
441 | buildConfigurationList = 721C796E1C5D426C008EFF59 /* Build configuration list for PBXNativeTarget "Ares-iOS" */;
442 | buildPhases = (
443 | 721C795B1C5D426C008EFF59 /* Sources */,
444 | 721C795C1C5D426C008EFF59 /* Frameworks */,
445 | 721C795D1C5D426C008EFF59 /* Resources */,
446 | 721C79A21C5D4302008EFF59 /* Embed Frameworks */,
447 | );
448 | buildRules = (
449 | );
450 | dependencies = (
451 | 721C799C1C5D4301008EFF59 /* PBXTargetDependency */,
452 | 721C79CA1C5D63C6008EFF59 /* PBXTargetDependency */,
453 | 7252DD871C5E127A00E681E1 /* PBXTargetDependency */,
454 | );
455 | name = "Ares-iOS";
456 | productName = "Ares-iOS";
457 | productReference = 721C795F1C5D426C008EFF59 /* Ares.app */;
458 | productType = "com.apple.product-type.application";
459 | };
460 | 721C79831C5D42DE008EFF59 /* AresKit-Mac */ = {
461 | isa = PBXNativeTarget;
462 | buildConfigurationList = 721C798D1C5D42DE008EFF59 /* Build configuration list for PBXNativeTarget "AresKit-Mac" */;
463 | buildPhases = (
464 | 721C797F1C5D42DE008EFF59 /* Sources */,
465 | 721C79801C5D42DE008EFF59 /* Frameworks */,
466 | 721C79811C5D42DE008EFF59 /* Headers */,
467 | 721C79821C5D42DE008EFF59 /* Resources */,
468 | );
469 | buildRules = (
470 | );
471 | dependencies = (
472 | );
473 | name = "AresKit-Mac";
474 | productName = AresKit;
475 | productReference = 721C79841C5D42DE008EFF59 /* AresKit.framework */;
476 | productType = "com.apple.product-type.framework";
477 | };
478 | 721C79951C5D4301008EFF59 /* AresKit-iOS */ = {
479 | isa = PBXNativeTarget;
480 | buildConfigurationList = 721C799F1C5D4302008EFF59 /* Build configuration list for PBXNativeTarget "AresKit-iOS" */;
481 | buildPhases = (
482 | 721C79911C5D4301008EFF59 /* Sources */,
483 | 721C79921C5D4301008EFF59 /* Frameworks */,
484 | 721C79931C5D4301008EFF59 /* Headers */,
485 | 721C79941C5D4301008EFF59 /* Resources */,
486 | );
487 | buildRules = (
488 | );
489 | dependencies = (
490 | );
491 | name = "AresKit-iOS";
492 | productName = "AresKit-iOS";
493 | productReference = 721C79961C5D4301008EFF59 /* AresKit.framework */;
494 | productType = "com.apple.product-type.framework";
495 | };
496 | /* End PBXNativeTarget section */
497 |
498 | /* Begin PBXProject section */
499 | 721C79241C5D418C008EFF59 /* Project object */ = {
500 | isa = PBXProject;
501 | attributes = {
502 | LastSwiftUpdateCheck = 0730;
503 | LastUpgradeCheck = 0730;
504 | ORGANIZATIONNAME = "Indragie Karunaratne";
505 | TargetAttributes = {
506 | 721C792B1C5D418C008EFF59 = {
507 | CreatedOnToolsVersion = 7.3;
508 | DevelopmentTeam = H73VKH7W9W;
509 | };
510 | 721C795E1C5D426C008EFF59 = {
511 | CreatedOnToolsVersion = 7.3;
512 | DevelopmentTeam = H73VKH7W9W;
513 | SystemCapabilities = {
514 | com.apple.Keychain = {
515 | enabled = 1;
516 | };
517 | };
518 | };
519 | 721C79831C5D42DE008EFF59 = {
520 | CreatedOnToolsVersion = 7.3;
521 | };
522 | 721C79951C5D4301008EFF59 = {
523 | CreatedOnToolsVersion = 7.3;
524 | };
525 | };
526 | };
527 | buildConfigurationList = 721C79271C5D418C008EFF59 /* Build configuration list for PBXProject "Ares" */;
528 | compatibilityVersion = "Xcode 3.2";
529 | developmentRegion = English;
530 | hasScannedForEncodings = 0;
531 | knownRegions = (
532 | en,
533 | Base,
534 | );
535 | mainGroup = 721C79231C5D418C008EFF59;
536 | productRefGroup = 721C792D1C5D418C008EFF59 /* Products */;
537 | projectDirPath = "";
538 | projectReferences = (
539 | {
540 | ProductGroup = 721C79AA1C5D63A1008EFF59 /* Products */;
541 | ProjectRef = 721C79A91C5D63A1008EFF59 /* Alamofire.xcodeproj */;
542 | },
543 | {
544 | ProductGroup = 7252DD7C1C5E126D00E681E1 /* Products */;
545 | ProjectRef = 7252DD7B1C5E126D00E681E1 /* DynamicFramework.xcodeproj */;
546 | },
547 | );
548 | projectRoot = "";
549 | targets = (
550 | 721C792B1C5D418C008EFF59 /* Ares-Mac */,
551 | 721C795E1C5D426C008EFF59 /* Ares-iOS */,
552 | 721C79831C5D42DE008EFF59 /* AresKit-Mac */,
553 | 721C79951C5D4301008EFF59 /* AresKit-iOS */,
554 | );
555 | };
556 | /* End PBXProject section */
557 |
558 | /* Begin PBXReferenceProxy section */
559 | 721C79B41C5D63A1008EFF59 /* Alamofire.framework */ = {
560 | isa = PBXReferenceProxy;
561 | fileType = wrapper.framework;
562 | path = Alamofire.framework;
563 | remoteRef = 721C79B31C5D63A1008EFF59 /* PBXContainerItemProxy */;
564 | sourceTree = BUILT_PRODUCTS_DIR;
565 | };
566 | 721C79B61C5D63A1008EFF59 /* Alamofire iOS Tests.xctest */ = {
567 | isa = PBXReferenceProxy;
568 | fileType = wrapper.cfbundle;
569 | path = "Alamofire iOS Tests.xctest";
570 | remoteRef = 721C79B51C5D63A1008EFF59 /* PBXContainerItemProxy */;
571 | sourceTree = BUILT_PRODUCTS_DIR;
572 | };
573 | 721C79B81C5D63A1008EFF59 /* Alamofire.framework */ = {
574 | isa = PBXReferenceProxy;
575 | fileType = wrapper.framework;
576 | path = Alamofire.framework;
577 | remoteRef = 721C79B71C5D63A1008EFF59 /* PBXContainerItemProxy */;
578 | sourceTree = BUILT_PRODUCTS_DIR;
579 | };
580 | 721C79BA1C5D63A1008EFF59 /* Alamofire OSX Tests.xctest */ = {
581 | isa = PBXReferenceProxy;
582 | fileType = wrapper.cfbundle;
583 | path = "Alamofire OSX Tests.xctest";
584 | remoteRef = 721C79B91C5D63A1008EFF59 /* PBXContainerItemProxy */;
585 | sourceTree = BUILT_PRODUCTS_DIR;
586 | };
587 | 721C79BC1C5D63A1008EFF59 /* Alamofire.framework */ = {
588 | isa = PBXReferenceProxy;
589 | fileType = wrapper.framework;
590 | path = Alamofire.framework;
591 | remoteRef = 721C79BB1C5D63A1008EFF59 /* PBXContainerItemProxy */;
592 | sourceTree = BUILT_PRODUCTS_DIR;
593 | };
594 | 721C79BE1C5D63A1008EFF59 /* Alamofire tvOS Tests.xctest */ = {
595 | isa = PBXReferenceProxy;
596 | fileType = wrapper.cfbundle;
597 | path = "Alamofire tvOS Tests.xctest";
598 | remoteRef = 721C79BD1C5D63A1008EFF59 /* PBXContainerItemProxy */;
599 | sourceTree = BUILT_PRODUCTS_DIR;
600 | };
601 | 721C79C01C5D63A1008EFF59 /* Alamofire.framework */ = {
602 | isa = PBXReferenceProxy;
603 | fileType = wrapper.framework;
604 | path = Alamofire.framework;
605 | remoteRef = 721C79BF1C5D63A1008EFF59 /* PBXContainerItemProxy */;
606 | sourceTree = BUILT_PRODUCTS_DIR;
607 | };
608 | 7252DD811C5E126D00E681E1 /* KVOController.framework */ = {
609 | isa = PBXReferenceProxy;
610 | fileType = wrapper.framework;
611 | path = KVOController.framework;
612 | remoteRef = 7252DD801C5E126D00E681E1 /* PBXContainerItemProxy */;
613 | sourceTree = BUILT_PRODUCTS_DIR;
614 | };
615 | 7252DD831C5E126D00E681E1 /* KVOController.framework */ = {
616 | isa = PBXReferenceProxy;
617 | fileType = wrapper.framework;
618 | path = KVOController.framework;
619 | remoteRef = 7252DD821C5E126D00E681E1 /* PBXContainerItemProxy */;
620 | sourceTree = BUILT_PRODUCTS_DIR;
621 | };
622 | /* End PBXReferenceProxy section */
623 |
624 | /* Begin PBXResourcesBuildPhase section */
625 | 721C792A1C5D418C008EFF59 /* Resources */ = {
626 | isa = PBXResourcesBuildPhase;
627 | buildActionMask = 2147483647;
628 | files = (
629 | 721C79F81C5D8404008EFF59 /* LoginWindowController.xib in Resources */,
630 | 721C79401C5D41D1008EFF59 /* Assets.xcassets in Resources */,
631 | 721C79441C5D41DD008EFF59 /* MainMenu.xib in Resources */,
632 | );
633 | runOnlyForDeploymentPostprocessing = 0;
634 | };
635 | 721C795D1C5D426C008EFF59 /* Resources */ = {
636 | isa = PBXResourcesBuildPhase;
637 | buildActionMask = 2147483647;
638 | files = (
639 | 721C79FC1C5D843E008EFF59 /* LoginViewController.xib in Resources */,
640 | 721C797D1C5D4294008EFF59 /* LaunchScreen.storyboard in Resources */,
641 | 7252DD7A1C5E0F5A00E681E1 /* ViewController.xib in Resources */,
642 | 721C79761C5D428E008EFF59 /* Assets.xcassets in Resources */,
643 | );
644 | runOnlyForDeploymentPostprocessing = 0;
645 | };
646 | 721C79821C5D42DE008EFF59 /* Resources */ = {
647 | isa = PBXResourcesBuildPhase;
648 | buildActionMask = 2147483647;
649 | files = (
650 | );
651 | runOnlyForDeploymentPostprocessing = 0;
652 | };
653 | 721C79941C5D4301008EFF59 /* Resources */ = {
654 | isa = PBXResourcesBuildPhase;
655 | buildActionMask = 2147483647;
656 | files = (
657 | );
658 | runOnlyForDeploymentPostprocessing = 0;
659 | };
660 | /* End PBXResourcesBuildPhase section */
661 |
662 | /* Begin PBXSourcesBuildPhase section */
663 | 721C79281C5D418C008EFF59 /* Sources */ = {
664 | isa = PBXSourcesBuildPhase;
665 | buildActionMask = 2147483647;
666 | files = (
667 | 721C793F1C5D41D1008EFF59 /* AppDelegate.swift in Sources */,
668 | 721C79F71C5D8404008EFF59 /* LoginWindowController.swift in Sources */,
669 | 7203D1C61C5DBA450011E3F0 /* StatusItemController.swift in Sources */,
670 | );
671 | runOnlyForDeploymentPostprocessing = 0;
672 | };
673 | 721C795B1C5D426C008EFF59 /* Sources */ = {
674 | isa = PBXSourcesBuildPhase;
675 | buildActionMask = 2147483647;
676 | files = (
677 | 721C79FB1C5D843E008EFF59 /* LoginViewController.swift in Sources */,
678 | 721C79781C5D428E008EFF59 /* ViewController.swift in Sources */,
679 | 7252DD891C5E14AA00E681E1 /* PreviewViewController.swift in Sources */,
680 | 7203D1C01C5DA0CD0011E3F0 /* NSDataExtensions.swift in Sources */,
681 | 721C79FE1C5D89A5008EFF59 /* UIAlertControllerExtensions.swift in Sources */,
682 | 7203D1BE1C5D9F690011E3F0 /* APNSManager.swift in Sources */,
683 | 721C79751C5D428E008EFF59 /* AppDelegate.swift in Sources */,
684 | );
685 | runOnlyForDeploymentPostprocessing = 0;
686 | };
687 | 721C797F1C5D42DE008EFF59 /* Sources */ = {
688 | isa = PBXSourcesBuildPhase;
689 | buildActionMask = 2147483647;
690 | files = (
691 | 7252DD691C5DF12C00E681E1 /* PushNotification.swift in Sources */,
692 | 721C79CC1C5D642F008EFF59 /* User.swift in Sources */,
693 | 7252DD711C5DFA8B00E681E1 /* IncomingFileTransfer.swift in Sources */,
694 | 721C79A61C5D62A3008EFF59 /* Client.swift in Sources */,
695 | 721C79D51C5D7276008EFF59 /* CreatedUser.swift in Sources */,
696 | 7203D1C31C5DAD580011E3F0 /* ConnectionManager.swift in Sources */,
697 | 721C79CF1C5D64F2008EFF59 /* JSONDeserializable.swift in Sources */,
698 | 721C79DC1C5D78B7008EFF59 /* CredentialStorage.swift in Sources */,
699 | 721C79D21C5D6514008EFF59 /* AccessToken.swift in Sources */,
700 | 721C79F31C5D7969008EFF59 /* Keychain.swift in Sources */,
701 | 7252DD741C5DFC5D00E681E1 /* FileTransferContext.swift in Sources */,
702 | 7203D1CA1C5DE20F0011E3F0 /* DeviceName.swift in Sources */,
703 | 7252DD771C5DFD4400E681E1 /* OutgoingFileTransfer.swift in Sources */,
704 | 721C7A001C5D9505008EFF59 /* RegisteredDevice.swift in Sources */,
705 | );
706 | runOnlyForDeploymentPostprocessing = 0;
707 | };
708 | 721C79911C5D4301008EFF59 /* Sources */ = {
709 | isa = PBXSourcesBuildPhase;
710 | buildActionMask = 2147483647;
711 | files = (
712 | 7252DD6A1C5DF12C00E681E1 /* PushNotification.swift in Sources */,
713 | 721C79CD1C5D642F008EFF59 /* User.swift in Sources */,
714 | 7252DD721C5DFA8B00E681E1 /* IncomingFileTransfer.swift in Sources */,
715 | 721C79A71C5D62A3008EFF59 /* Client.swift in Sources */,
716 | 721C79D61C5D7276008EFF59 /* CreatedUser.swift in Sources */,
717 | 7203D1C41C5DAD580011E3F0 /* ConnectionManager.swift in Sources */,
718 | 721C79D01C5D64F2008EFF59 /* JSONDeserializable.swift in Sources */,
719 | 721C79DD1C5D78B7008EFF59 /* CredentialStorage.swift in Sources */,
720 | 721C79D31C5D6514008EFF59 /* AccessToken.swift in Sources */,
721 | 721C79F41C5D7969008EFF59 /* Keychain.swift in Sources */,
722 | 7252DD751C5DFC5D00E681E1 /* FileTransferContext.swift in Sources */,
723 | 7203D1CB1C5DE20F0011E3F0 /* DeviceName.swift in Sources */,
724 | 7252DD781C5DFD4400E681E1 /* OutgoingFileTransfer.swift in Sources */,
725 | 721C7A011C5D9505008EFF59 /* RegisteredDevice.swift in Sources */,
726 | );
727 | runOnlyForDeploymentPostprocessing = 0;
728 | };
729 | /* End PBXSourcesBuildPhase section */
730 |
731 | /* Begin PBXTargetDependency section */
732 | 721C798A1C5D42DE008EFF59 /* PBXTargetDependency */ = {
733 | isa = PBXTargetDependency;
734 | target = 721C79831C5D42DE008EFF59 /* AresKit-Mac */;
735 | targetProxy = 721C79891C5D42DE008EFF59 /* PBXContainerItemProxy */;
736 | };
737 | 721C799C1C5D4301008EFF59 /* PBXTargetDependency */ = {
738 | isa = PBXTargetDependency;
739 | target = 721C79951C5D4301008EFF59 /* AresKit-iOS */;
740 | targetProxy = 721C799B1C5D4301008EFF59 /* PBXContainerItemProxy */;
741 | };
742 | 721C79C61C5D63BF008EFF59 /* PBXTargetDependency */ = {
743 | isa = PBXTargetDependency;
744 | name = "Alamofire OSX";
745 | targetProxy = 721C79C51C5D63BF008EFF59 /* PBXContainerItemProxy */;
746 | };
747 | 721C79CA1C5D63C6008EFF59 /* PBXTargetDependency */ = {
748 | isa = PBXTargetDependency;
749 | name = "Alamofire iOS";
750 | targetProxy = 721C79C91C5D63C6008EFF59 /* PBXContainerItemProxy */;
751 | };
752 | 7252DD871C5E127A00E681E1 /* PBXTargetDependency */ = {
753 | isa = PBXTargetDependency;
754 | name = "KVOController-iOS";
755 | targetProxy = 7252DD861C5E127A00E681E1 /* PBXContainerItemProxy */;
756 | };
757 | /* End PBXTargetDependency section */
758 |
759 | /* Begin PBXVariantGroup section */
760 | 721C79421C5D41DD008EFF59 /* MainMenu.xib */ = {
761 | isa = PBXVariantGroup;
762 | children = (
763 | 721C79431C5D41DD008EFF59 /* Base */,
764 | );
765 | name = MainMenu.xib;
766 | sourceTree = "";
767 | };
768 | 721C79791C5D4294008EFF59 /* LaunchScreen.storyboard */ = {
769 | isa = PBXVariantGroup;
770 | children = (
771 | 721C797A1C5D4294008EFF59 /* Base */,
772 | );
773 | name = LaunchScreen.storyboard;
774 | sourceTree = "";
775 | };
776 | /* End PBXVariantGroup section */
777 |
778 | /* Begin XCBuildConfiguration section */
779 | 721C79371C5D418C008EFF59 /* Debug */ = {
780 | isa = XCBuildConfiguration;
781 | buildSettings = {
782 | ALWAYS_SEARCH_USER_PATHS = NO;
783 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
784 | CLANG_CXX_LIBRARY = "libc++";
785 | CLANG_ENABLE_MODULES = YES;
786 | CLANG_ENABLE_OBJC_ARC = YES;
787 | CLANG_WARN_BOOL_CONVERSION = YES;
788 | CLANG_WARN_CONSTANT_CONVERSION = YES;
789 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
790 | CLANG_WARN_EMPTY_BODY = YES;
791 | CLANG_WARN_ENUM_CONVERSION = YES;
792 | CLANG_WARN_INT_CONVERSION = YES;
793 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
794 | CLANG_WARN_UNREACHABLE_CODE = YES;
795 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
796 | CODE_SIGN_IDENTITY = "-";
797 | COPY_PHASE_STRIP = NO;
798 | DEBUG_INFORMATION_FORMAT = dwarf;
799 | ENABLE_STRICT_OBJC_MSGSEND = YES;
800 | ENABLE_TESTABILITY = YES;
801 | GCC_C_LANGUAGE_STANDARD = gnu99;
802 | GCC_DYNAMIC_NO_PIC = NO;
803 | GCC_NO_COMMON_BLOCKS = YES;
804 | GCC_OPTIMIZATION_LEVEL = 0;
805 | GCC_PREPROCESSOR_DEFINITIONS = (
806 | "DEBUG=1",
807 | "$(inherited)",
808 | );
809 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
810 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
811 | GCC_WARN_UNDECLARED_SELECTOR = YES;
812 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
813 | GCC_WARN_UNUSED_FUNCTION = YES;
814 | GCC_WARN_UNUSED_VARIABLE = YES;
815 | MACOSX_DEPLOYMENT_TARGET = 10.11;
816 | MTL_ENABLE_DEBUG_INFO = YES;
817 | ONLY_ACTIVE_ARCH = YES;
818 | SDKROOT = macosx;
819 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
820 | };
821 | name = Debug;
822 | };
823 | 721C79381C5D418C008EFF59 /* Release */ = {
824 | isa = XCBuildConfiguration;
825 | buildSettings = {
826 | ALWAYS_SEARCH_USER_PATHS = NO;
827 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
828 | CLANG_CXX_LIBRARY = "libc++";
829 | CLANG_ENABLE_MODULES = YES;
830 | CLANG_ENABLE_OBJC_ARC = YES;
831 | CLANG_WARN_BOOL_CONVERSION = YES;
832 | CLANG_WARN_CONSTANT_CONVERSION = YES;
833 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
834 | CLANG_WARN_EMPTY_BODY = YES;
835 | CLANG_WARN_ENUM_CONVERSION = YES;
836 | CLANG_WARN_INT_CONVERSION = YES;
837 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
838 | CLANG_WARN_UNREACHABLE_CODE = YES;
839 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
840 | CODE_SIGN_IDENTITY = "-";
841 | COPY_PHASE_STRIP = NO;
842 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
843 | ENABLE_NS_ASSERTIONS = NO;
844 | ENABLE_STRICT_OBJC_MSGSEND = YES;
845 | GCC_C_LANGUAGE_STANDARD = gnu99;
846 | GCC_NO_COMMON_BLOCKS = YES;
847 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
848 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
849 | GCC_WARN_UNDECLARED_SELECTOR = YES;
850 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
851 | GCC_WARN_UNUSED_FUNCTION = YES;
852 | GCC_WARN_UNUSED_VARIABLE = YES;
853 | MACOSX_DEPLOYMENT_TARGET = 10.11;
854 | MTL_ENABLE_DEBUG_INFO = NO;
855 | SDKROOT = macosx;
856 | };
857 | name = Release;
858 | };
859 | 721C793A1C5D418C008EFF59 /* Debug */ = {
860 | isa = XCBuildConfiguration;
861 | buildSettings = {
862 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
863 | CLANG_ENABLE_MODULES = YES;
864 | CODE_SIGN_IDENTITY = "Developer ID Application";
865 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
866 | COMBINE_HIDPI_IMAGES = YES;
867 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
868 | INFOPLIST_FILE = Mac/Info.plist;
869 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
870 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.ares-mac";
871 | PRODUCT_NAME = Ares;
872 | PROVISIONING_PROFILE = "";
873 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
874 | };
875 | name = Debug;
876 | };
877 | 721C793B1C5D418C008EFF59 /* Release */ = {
878 | isa = XCBuildConfiguration;
879 | buildSettings = {
880 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
881 | CLANG_ENABLE_MODULES = YES;
882 | CODE_SIGN_IDENTITY = "Developer ID Application";
883 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer";
884 | COMBINE_HIDPI_IMAGES = YES;
885 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
886 | INFOPLIST_FILE = Mac/Info.plist;
887 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
888 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.ares-mac";
889 | PRODUCT_NAME = Ares;
890 | PROVISIONING_PROFILE = "";
891 | };
892 | name = Release;
893 | };
894 | 721C796F1C5D426C008EFF59 /* Debug */ = {
895 | isa = XCBuildConfiguration;
896 | buildSettings = {
897 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
898 | CLANG_ENABLE_MODULES = YES;
899 | CODE_SIGN_ENTITLEMENTS = Ares.entitlements;
900 | CODE_SIGN_IDENTITY = "iPhone Developer";
901 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
902 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
903 | INFOPLIST_FILE = iOS/Info.plist;
904 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
905 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
906 | PRODUCT_BUNDLE_IDENTIFIER = com.indragie.ares;
907 | PRODUCT_NAME = Ares;
908 | SDKROOT = iphoneos;
909 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
910 | };
911 | name = Debug;
912 | };
913 | 721C79701C5D426C008EFF59 /* Release */ = {
914 | isa = XCBuildConfiguration;
915 | buildSettings = {
916 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
917 | CLANG_ENABLE_MODULES = YES;
918 | CODE_SIGN_ENTITLEMENTS = Ares.entitlements;
919 | CODE_SIGN_IDENTITY = "iPhone Developer";
920 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
921 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
922 | INFOPLIST_FILE = iOS/Info.plist;
923 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
924 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
925 | PRODUCT_BUNDLE_IDENTIFIER = com.indragie.ares;
926 | PRODUCT_NAME = Ares;
927 | SDKROOT = iphoneos;
928 | VALIDATE_PRODUCT = YES;
929 | };
930 | name = Release;
931 | };
932 | 721C798E1C5D42DE008EFF59 /* Debug */ = {
933 | isa = XCBuildConfiguration;
934 | buildSettings = {
935 | CLANG_ENABLE_MODULES = YES;
936 | COMBINE_HIDPI_IMAGES = YES;
937 | CURRENT_PROJECT_VERSION = 1;
938 | DEFINES_MODULE = YES;
939 | DYLIB_COMPATIBILITY_VERSION = 1;
940 | DYLIB_CURRENT_VERSION = 1;
941 | DYLIB_INSTALL_NAME_BASE = "@rpath";
942 | FRAMEWORK_VERSION = A;
943 | INFOPLIST_FILE = AresKit/Info.plist;
944 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
945 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
946 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.AresKit-Mac";
947 | PRODUCT_NAME = AresKit;
948 | SKIP_INSTALL = YES;
949 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
950 | VERSIONING_SYSTEM = "apple-generic";
951 | VERSION_INFO_PREFIX = "";
952 | };
953 | name = Debug;
954 | };
955 | 721C798F1C5D42DE008EFF59 /* Release */ = {
956 | isa = XCBuildConfiguration;
957 | buildSettings = {
958 | CLANG_ENABLE_MODULES = YES;
959 | COMBINE_HIDPI_IMAGES = YES;
960 | CURRENT_PROJECT_VERSION = 1;
961 | DEFINES_MODULE = YES;
962 | DYLIB_COMPATIBILITY_VERSION = 1;
963 | DYLIB_CURRENT_VERSION = 1;
964 | DYLIB_INSTALL_NAME_BASE = "@rpath";
965 | FRAMEWORK_VERSION = A;
966 | INFOPLIST_FILE = AresKit/Info.plist;
967 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
968 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
969 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.AresKit-Mac";
970 | PRODUCT_NAME = AresKit;
971 | SKIP_INSTALL = YES;
972 | VERSIONING_SYSTEM = "apple-generic";
973 | VERSION_INFO_PREFIX = "";
974 | };
975 | name = Release;
976 | };
977 | 721C79A01C5D4302008EFF59 /* Debug */ = {
978 | isa = XCBuildConfiguration;
979 | buildSettings = {
980 | CLANG_ENABLE_MODULES = YES;
981 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
982 | CURRENT_PROJECT_VERSION = 1;
983 | DEFINES_MODULE = YES;
984 | DYLIB_COMPATIBILITY_VERSION = 1;
985 | DYLIB_CURRENT_VERSION = 1;
986 | DYLIB_INSTALL_NAME_BASE = "@rpath";
987 | INFOPLIST_FILE = AresKit/Info.plist;
988 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
989 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
990 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
991 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.AresKit-iOS";
992 | PRODUCT_NAME = AresKit;
993 | SDKROOT = iphoneos;
994 | SKIP_INSTALL = YES;
995 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
996 | TARGETED_DEVICE_FAMILY = "1,2";
997 | VERSIONING_SYSTEM = "apple-generic";
998 | VERSION_INFO_PREFIX = "";
999 | };
1000 | name = Debug;
1001 | };
1002 | 721C79A11C5D4302008EFF59 /* Release */ = {
1003 | isa = XCBuildConfiguration;
1004 | buildSettings = {
1005 | CLANG_ENABLE_MODULES = YES;
1006 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
1007 | CURRENT_PROJECT_VERSION = 1;
1008 | DEFINES_MODULE = YES;
1009 | DYLIB_COMPATIBILITY_VERSION = 1;
1010 | DYLIB_CURRENT_VERSION = 1;
1011 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1012 | INFOPLIST_FILE = AresKit/Info.plist;
1013 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1014 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
1015 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1016 | PRODUCT_BUNDLE_IDENTIFIER = "com.indragie.AresKit-iOS";
1017 | PRODUCT_NAME = AresKit;
1018 | SDKROOT = iphoneos;
1019 | SKIP_INSTALL = YES;
1020 | TARGETED_DEVICE_FAMILY = "1,2";
1021 | VALIDATE_PRODUCT = YES;
1022 | VERSIONING_SYSTEM = "apple-generic";
1023 | VERSION_INFO_PREFIX = "";
1024 | };
1025 | name = Release;
1026 | };
1027 | /* End XCBuildConfiguration section */
1028 |
1029 | /* Begin XCConfigurationList section */
1030 | 721C79271C5D418C008EFF59 /* Build configuration list for PBXProject "Ares" */ = {
1031 | isa = XCConfigurationList;
1032 | buildConfigurations = (
1033 | 721C79371C5D418C008EFF59 /* Debug */,
1034 | 721C79381C5D418C008EFF59 /* Release */,
1035 | );
1036 | defaultConfigurationIsVisible = 0;
1037 | defaultConfigurationName = Release;
1038 | };
1039 | 721C79391C5D418C008EFF59 /* Build configuration list for PBXNativeTarget "Ares-Mac" */ = {
1040 | isa = XCConfigurationList;
1041 | buildConfigurations = (
1042 | 721C793A1C5D418C008EFF59 /* Debug */,
1043 | 721C793B1C5D418C008EFF59 /* Release */,
1044 | );
1045 | defaultConfigurationIsVisible = 0;
1046 | defaultConfigurationName = Release;
1047 | };
1048 | 721C796E1C5D426C008EFF59 /* Build configuration list for PBXNativeTarget "Ares-iOS" */ = {
1049 | isa = XCConfigurationList;
1050 | buildConfigurations = (
1051 | 721C796F1C5D426C008EFF59 /* Debug */,
1052 | 721C79701C5D426C008EFF59 /* Release */,
1053 | );
1054 | defaultConfigurationIsVisible = 0;
1055 | defaultConfigurationName = Release;
1056 | };
1057 | 721C798D1C5D42DE008EFF59 /* Build configuration list for PBXNativeTarget "AresKit-Mac" */ = {
1058 | isa = XCConfigurationList;
1059 | buildConfigurations = (
1060 | 721C798E1C5D42DE008EFF59 /* Debug */,
1061 | 721C798F1C5D42DE008EFF59 /* Release */,
1062 | );
1063 | defaultConfigurationIsVisible = 0;
1064 | defaultConfigurationName = Release;
1065 | };
1066 | 721C799F1C5D4302008EFF59 /* Build configuration list for PBXNativeTarget "AresKit-iOS" */ = {
1067 | isa = XCConfigurationList;
1068 | buildConfigurations = (
1069 | 721C79A01C5D4302008EFF59 /* Debug */,
1070 | 721C79A11C5D4302008EFF59 /* Release */,
1071 | );
1072 | defaultConfigurationIsVisible = 0;
1073 | defaultConfigurationName = Release;
1074 | };
1075 | /* End XCConfigurationList section */
1076 | };
1077 | rootObject = 721C79241C5D418C008EFF59 /* Project object */;
1078 | }
1079 |
--------------------------------------------------------------------------------
/client/Ares.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/client/AresKit/AccessToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AccessToken.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public struct AccessToken: CustomStringConvertible, JSONDeserializable {
10 | public let username: String
11 | public let token: String
12 |
13 | internal init(username: String, token: String) {
14 | self.username = username
15 | self.token = token
16 | }
17 |
18 | // MARK: CustomStringConvertible
19 |
20 | public var description: String {
21 | return "AccessToken{username=\(username), token=\(token)}"
22 | }
23 |
24 | // MARK: JSONDeserializable
25 |
26 | public init?(JSON: JSONDictionary) {
27 | if let username = JSON["username"] as? String,
28 | token = JSON["token"] as? String {
29 | self.username = username
30 | self.token = token
31 | } else {
32 | self.username = ""
33 | self.token = ""
34 | return nil
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/client/AresKit/AresKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // AresKit.h
3 | // AresKit
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for AresKit.
12 | FOUNDATION_EXPORT double AresKitVersionNumber;
13 |
14 | //! Project version string for AresKit.
15 | FOUNDATION_EXPORT const unsigned char AresKitVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/client/AresKit/Client.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Client.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | private let UserDefaultsDeviceUUIDKey = "deviceUUID";
13 | private let DefaultAPIURL = NSURL(string: "http://yourappname.heroku.com")!
14 |
15 | public final class Client {
16 | public static let ErrorDomain = "AresKitClientErrorDomain"
17 | public static let APIErrorKey = "api_error"
18 | public enum ErrorCode: Int {
19 | case APIError
20 | case InvalidJSONResponse
21 | }
22 |
23 | private let manager: Manager
24 | private let URL: NSURL
25 |
26 | public init(URL: NSURL = DefaultAPIURL, configuration: NSURLSessionConfiguration = .defaultSessionConfiguration()) {
27 | self.URL = URL
28 | self.manager = Manager(configuration: configuration)
29 | }
30 |
31 | // MARK: API
32 |
33 | public func register(user: User, completionHandler: Result -> Void) {
34 | let request = Request(
35 | method: .POST,
36 | path: "/register",
37 | parameters: [
38 | "username": user.username,
39 | "password": user.password
40 | ]
41 | )
42 | requestModel(request, completionHandler: completionHandler)
43 | }
44 |
45 | public func authenticate(user: User, completionHandler: Result -> Void) {
46 | let request = Request(
47 | method: .POST,
48 | path: "/authenticate",
49 | parameters: [
50 | "username": user.username,
51 | "password": user.password
52 | ]
53 | )
54 | requestModel(request, completionHandler: completionHandler)
55 | }
56 |
57 | public func registerDevice(accessToken: AccessToken, pushToken: String? = nil, completionHandler: Result -> Void) {
58 | var parameters = [
59 | "uuid": deviceUUID,
60 | "device_name": getDeviceName(),
61 | "token": accessToken.token
62 | ]
63 | if let pushToken = pushToken {
64 | parameters["push_token"] = pushToken
65 | }
66 | let request = Request(
67 | method: .POST,
68 | path: "/register_device",
69 | parameters: parameters
70 | )
71 | requestModel(request, completionHandler: completionHandler)
72 | }
73 |
74 | public func getDevices(accessToken: AccessToken, completionHandler: Result<[RegisteredDevice], NSError> -> Void) {
75 | let request = Request(method: .GET, path: "/devices", parameters: [
76 | "token": accessToken.token
77 | ])
78 | requestModelArray(request, completionHandler: completionHandler)
79 | }
80 |
81 | public func send(accessToken: AccessToken, filePath: String, device: RegisteredDevice, completionHandler: Result -> Void) {
82 | let request = Request(method: .POST, path: "/send", parameters: [
83 | "token": accessToken.token,
84 | "file_path": filePath,
85 | "to_id": device.uuid,
86 | "from_id": deviceUUID
87 | ])
88 | requestVoid(request, completionHandler: completionHandler)
89 | }
90 |
91 | public var deviceUUID: String {
92 | let ud = NSUserDefaults.standardUserDefaults()
93 | let UUID: String
94 | if let storedUUID = ud.stringForKey(UserDefaultsDeviceUUIDKey) {
95 | UUID = storedUUID
96 | } else {
97 | UUID = NSUUID().UUIDString
98 | ud.setObject(UUID, forKey: UserDefaultsDeviceUUIDKey)
99 | }
100 | return UUID
101 | }
102 |
103 | // MARK: Primitives
104 |
105 | private struct Request {
106 | let method: Alamofire.Method
107 | let path: String
108 | let parameters: [String: AnyObject]
109 | }
110 |
111 | private static let InvalidJSONResponseError = NSError(domain: ErrorDomain, code: ErrorCode.InvalidJSONResponse.rawValue, userInfo: nil)
112 |
113 | private func requestModel(request: Request, completionHandler: Result -> Void) {
114 | requestJSON(request) { result in
115 | switch result {
116 | case let .Success(json):
117 | if let json = json as? JSONDictionary, model = T(JSON: json) {
118 | completionHandler(.Success(model))
119 | } else {
120 | completionHandler(.Failure(self.dynamicType.InvalidJSONResponseError))
121 | }
122 | case let .Failure(error):
123 | completionHandler(.Failure(error))
124 | }
125 | }
126 | }
127 |
128 | private func requestModelArray(request: Request, completionHandler: Result<[T], NSError> -> Void) {
129 | requestJSON(request) { result in
130 | switch result {
131 | case let .Success(json):
132 | if let jsonArray = json as? [JSONDictionary] {
133 | var models = [T]()
134 | for deviceDict in jsonArray {
135 | if let model = T(JSON: deviceDict) {
136 | models.append(model)
137 | } else {
138 | completionHandler(.Failure(self.dynamicType.InvalidJSONResponseError))
139 | return
140 | }
141 | }
142 | completionHandler(.Success(models))
143 | } else {
144 | completionHandler(.Failure(self.dynamicType.InvalidJSONResponseError))
145 | }
146 | case let .Failure(error):
147 | completionHandler(.Failure(error))
148 | }
149 | }
150 | }
151 |
152 | private func requestJSON(request: Request, completionHandler: Result -> Void) {
153 | guard let components = NSURLComponents(URL: URL, resolvingAgainstBaseURL: false) else {
154 | fatalError("Invalid API base URL: \(URL)")
155 | }
156 | components.path = request.path
157 | guard let requestURL = components.URL else {
158 | fatalError("Unable to construct request URL")
159 | }
160 | manager.request(request.method, requestURL, parameters: request.parameters, encoding: .URL)
161 | .responseJSON { response in
162 | switch response.result {
163 | case let .Success(responseObject):
164 | if let json = responseObject as? JSONDictionary {
165 | if let success = json["success"] as? Bool,
166 | result = json["result"] where success {
167 | completionHandler(.Success(result))
168 | } else {
169 | let error = self.dynamicType.constructAPIErrorFromJSON(json)
170 | completionHandler(.Failure(error))
171 | }
172 | } else {
173 | completionHandler(.Failure(self.dynamicType.InvalidJSONResponseError))
174 | }
175 | case let .Failure(error):
176 | completionHandler(.Failure(error))
177 | }
178 | }
179 | }
180 |
181 | private func requestVoid(request: Request, completionHandler: Result -> Void) {
182 | requestJSON(request) { result in
183 | switch result {
184 | case .Success:
185 | completionHandler(.Success(()))
186 | case let .Failure(error):
187 | completionHandler(.Failure(error))
188 | }
189 | }
190 | }
191 |
192 | private static func constructAPIErrorFromJSON(json: JSONDictionary) -> NSError {
193 | var userInfo = [String: AnyObject]()
194 | if let error = json["error"] as? String {
195 | userInfo[APIErrorKey] = error
196 | if let description = localizedDescriptionForAPIError(error) {
197 | userInfo[NSLocalizedDescriptionKey] = description
198 | }
199 | }
200 | return NSError(domain: ErrorDomain, code: ErrorCode.APIError.rawValue, userInfo: userInfo)
201 | }
202 | }
203 |
204 | private func localizedDescriptionForAPIError(error: String) -> String? {
205 | switch error {
206 | case "USER_EXISTS":
207 | return "A user with the specified username already exists.";
208 | case "USER_DOES_NOT_EXIST":
209 | return "A user with the specified username does not exist.";
210 | case "PASSWORD_INCORRECT":
211 | return "The specified password is incorrect.";
212 | case "INVALID_TOKEN":
213 | return "The specified access token is invalid.";
214 | default: return nil
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/client/AresKit/ConnectionManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConnectionManager.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MultipeerConnectivity
11 |
12 | public protocol ConnectionManagerDelegate: AnyObject {
13 | func connectionManager(manager: ConnectionManager, didUpdateDevices devices: [Device])
14 | func connectionManager(manager: ConnectionManager, didFailWithError error: NSError)
15 | func connectionManager(manager: ConnectionManager, willBeginIncomingFileTransfer transfer: IncomingFileTransfer)
16 | func connectionManager(manager: ConnectionManager, willBeginOutgoingFileTransfer transfer: OutgoingFileTransfer)
17 | }
18 |
19 | public struct Device: CustomStringConvertible {
20 | public enum Availability {
21 | case None
22 | case Local
23 | case Remote
24 | }
25 |
26 | public var description: String {
27 | return "Device{registeredDevice=\(registeredDevice), availability=\(availability)}"
28 | }
29 |
30 | public let registeredDevice: RegisteredDevice
31 | public let availability: Availability
32 | internal let peerID: MCPeerID?
33 |
34 | internal init(registeredDevice: RegisteredDevice, availability: Availability, peerID: MCPeerID? = nil) {
35 | self.registeredDevice = registeredDevice
36 | self.availability = availability
37 | self.peerID = peerID
38 | }
39 | }
40 |
41 | private let ServiceType = "ares-ft";
42 | private let DiscoveryUUIDKey = "uuid";
43 |
44 | @objc public final class ConnectionManager: NSObject, MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, IncomingFileTransferDelegate, OutgoingFileTransferDelegate {
45 | private let client: Client
46 | private let token: AccessToken
47 | private let localPeer: MCPeerID
48 | private let advertiser: MCNearbyServiceAdvertiser
49 | private let browser: MCNearbyServiceBrowser
50 | private var peerIDToUUIDMap = [MCPeerID: String]()
51 | private var UUIDToPeerIDMap = [String: MCPeerID]()
52 | private var UUIDToNotificationMap = [String: [PushNotification]]()
53 | private var activeTransfers = [AnyObject]()
54 | private var isMonitoring: Bool = false
55 |
56 | private(set) public var devices = [Device]() {
57 | didSet {
58 | delegate?.connectionManager(self, didUpdateDevices: devices)
59 | }
60 | }
61 |
62 | public weak var delegate: ConnectionManagerDelegate?
63 | public weak var incomingFileTransferDelegate: IncomingFileTransferDelegate?
64 | public weak var outgoingFileTransferDelegate: OutgoingFileTransferDelegate?
65 |
66 | public init(client: Client, token: AccessToken) {
67 | self.client = client
68 | self.token = token
69 |
70 | localPeer = MCPeerID(displayName: getDeviceName())
71 | let discoveryInfo = [DiscoveryUUIDKey: client.deviceUUID]
72 | advertiser = MCNearbyServiceAdvertiser(peer: localPeer, discoveryInfo: discoveryInfo, serviceType: ServiceType)
73 | browser = MCNearbyServiceBrowser(peer: localPeer, serviceType: ServiceType)
74 |
75 | super.init()
76 |
77 | advertiser.delegate = self
78 | browser.delegate = self
79 |
80 | #if os(iOS)
81 | let nc = NSNotificationCenter.defaultCenter()
82 | nc.addObserver(self, selector: "appWillResignActive:", name: UIApplicationWillResignActiveNotification, object: nil)
83 | nc.addObserver(self, selector: "appDidBecomeActive:", name: UIApplicationDidBecomeActiveNotification, object: nil)
84 | #endif
85 | }
86 |
87 | deinit {
88 | #if os(iOS)
89 | NSNotificationCenter.defaultCenter().removeObserver(self)
90 | #endif
91 | }
92 |
93 | public func getDeviceList(completionHandler: Void -> Void) {
94 | client.getDevices(token) { result in
95 | switch result {
96 | case let .Success(registeredDevices):
97 | self.devices = registeredDevices
98 | .filter {
99 | return $0.uuid != self.client.deviceUUID
100 | }.map {
101 | Device(registeredDevice: $0, availability: .None)
102 | }
103 | completionHandler()
104 | case let .Failure(error):
105 | self.delegate?.connectionManager(self, didFailWithError: error)
106 | }
107 | }
108 | }
109 |
110 | public func startMonitoring() {
111 | advertiser.startAdvertisingPeer()
112 | browser.startBrowsingForPeers()
113 | isMonitoring = true
114 | }
115 |
116 | public func stopMonitoring() {
117 | advertiser.stopAdvertisingPeer()
118 | browser.stopBrowsingForPeers()
119 | isMonitoring = false
120 | }
121 |
122 | public func queueNotification(notification: PushNotification) {
123 | if let peerID = UUIDToPeerIDMap[notification.deviceUUID] {
124 | requestTransferFromPeer(peerID, filePath: notification.filePath)
125 | } else {
126 | var notifications = UUIDToNotificationMap[notification.deviceUUID] ?? []
127 | notifications.append(notification)
128 | UUIDToNotificationMap[notification.deviceUUID] = notifications
129 | }
130 | }
131 |
132 | // MARK: Notifications
133 |
134 | #if os(iOS)
135 | @objc private func appWillResignActive(notification: NSNotification) {
136 | advertiser.stopAdvertisingPeer()
137 | browser.stopBrowsingForPeers()
138 | peerIDToUUIDMap.removeAll()
139 | UUIDToPeerIDMap.removeAll()
140 | }
141 |
142 | @objc private func appDidBecomeActive(notification: NSNotification) {
143 | guard isMonitoring else { return }
144 | advertiser.startAdvertisingPeer()
145 | browser.startBrowsingForPeers()
146 | }
147 | #endif
148 |
149 | // MARK: Transfers
150 |
151 | func requestTransferFromPeer(peerID: MCPeerID, filePath: String) {
152 | let context = FileTransferContext(filePath: filePath)
153 | let transfer = IncomingFileTransfer(context: context, localPeerID: localPeer, remotePeerID: peerID)
154 | transfer.delegate = self
155 | activeTransfers.append(transfer)
156 |
157 | delegate?.connectionManager(self, willBeginIncomingFileTransfer: transfer)
158 |
159 | browser.invitePeer(peerID, toSession: transfer.session, withContext: context.archive(), timeout: 30)
160 | }
161 |
162 | // MARK: MCNearbyServiceAdvertiserDelegate
163 |
164 | public func advertiser(advertiser: MCNearbyServiceAdvertiser, didNotStartAdvertisingPeer error: NSError) {
165 | delegate?.connectionManager(self, didFailWithError: error)
166 | }
167 |
168 | public func advertiser(advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: NSData?, invitationHandler: (Bool, MCSession) -> Void) {
169 | guard let context = context.flatMap(FileTransferContext.init) else { return }
170 |
171 | let transfer = OutgoingFileTransfer(context: context, localPeerID: localPeer, remotePeerID: peerID)
172 | transfer.delegate = self
173 | activeTransfers.append(transfer)
174 | delegate?.connectionManager(self, willBeginOutgoingFileTransfer: transfer)
175 |
176 | invitationHandler(true, transfer.session)
177 | }
178 |
179 | // MARK: MCNearbyServiceBrowserDelegate
180 |
181 | public func browser(browser: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: NSError) {
182 | delegate?.connectionManager(self, didFailWithError: error)
183 | }
184 |
185 | public func browser(browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
186 | guard let uuid = info?[DiscoveryUUIDKey] else { return }
187 | guard let (index, device) = findDeviceWithUUID(uuid) else { return }
188 |
189 | peerIDToUUIDMap[peerID] = uuid
190 | UUIDToPeerIDMap[uuid] = peerID
191 |
192 | let newDevice = Device(registeredDevice: device.registeredDevice, availability: .Local, peerID: peerID)
193 | devices[index] = newDevice
194 |
195 | if let notifications = UUIDToNotificationMap[uuid] {
196 | for notification in notifications {
197 | requestTransferFromPeer(peerID, filePath: notification.filePath)
198 | }
199 | UUIDToNotificationMap.removeValueForKey(uuid)
200 | }
201 | }
202 |
203 | public func browser(browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
204 | guard let uuid = peerIDToUUIDMap[peerID] else { return }
205 | guard let (index, device) = findDeviceWithUUID(uuid) else { return }
206 |
207 | peerIDToUUIDMap.removeValueForKey(peerID)
208 | UUIDToPeerIDMap.removeValueForKey(uuid)
209 |
210 | let newDevice = Device(registeredDevice: device.registeredDevice, availability: .None)
211 | devices[index] = newDevice
212 | }
213 |
214 | private func findDeviceWithUUID(uuid: String) -> (Int, Device)? {
215 | for (index, device) in devices.enumerate() {
216 | if device.registeredDevice.uuid == uuid {
217 | return (index, device)
218 | }
219 | }
220 | return nil
221 | }
222 |
223 | private func removeActiveTransfer(transfer: AnyObject) {
224 | if let index = activeTransfers.indexOf({ $0 === transfer }) {
225 | activeTransfers.removeAtIndex(index)
226 | }
227 | }
228 |
229 | // MARK: IncomingFileTransferDelegate
230 |
231 | public func incomingFileTransfer(transfer: IncomingFileTransfer, didReceiveFileWithName name: String, URL: NSURL) {
232 | removeActiveTransfer(transfer)
233 | incomingFileTransferDelegate?.incomingFileTransfer(transfer, didReceiveFileWithName: name, URL: URL)
234 | }
235 |
236 | public func incomingFileTransfer(transfer: IncomingFileTransfer, didFailToReceiveFileWithName name: String, error: NSError) {
237 | removeActiveTransfer(transfer)
238 | incomingFileTransferDelegate?.incomingFileTransfer(transfer, didFailToReceiveFileWithName: name, error: error)
239 | }
240 |
241 | public func incomingFileTransfer(transfer: IncomingFileTransfer, didStartReceivingFileWithName name: String, progress: NSProgress) {
242 | incomingFileTransferDelegate?.incomingFileTransfer(transfer, didStartReceivingFileWithName: name, progress: progress)
243 | }
244 |
245 | // MARK: OutgoingFileTransferDelegate
246 |
247 | public func outgoingFileTransferDidComplete(transfer: OutgoingFileTransfer) {
248 | removeActiveTransfer(transfer)
249 | outgoingFileTransferDelegate?.outgoingFileTransferDidComplete(transfer)
250 | }
251 |
252 | public func outgoingFileTransfer(transfer: OutgoingFileTransfer, didFailWithError error: NSError) {
253 | removeActiveTransfer(transfer)
254 | outgoingFileTransferDelegate?.outgoingFileTransfer(transfer, didFailWithError: error)
255 | }
256 |
257 | public func outgoingFileTransfer(transfer: OutgoingFileTransfer, didStartWithProgress progress: NSProgress) {
258 | outgoingFileTransferDelegate?.outgoingFileTransfer(transfer, didStartWithProgress: progress)
259 | }
260 | }
261 |
--------------------------------------------------------------------------------
/client/AresKit/CreatedUser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CreatedUser.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public struct CreatedUser: CustomStringConvertible, JSONDeserializable {
10 | public let username: String
11 |
12 | // MARK: CustomStringConvertible
13 |
14 | public var description: String {
15 | return "CreatedUser{username=\(username)}"
16 | }
17 |
18 | // MARK: JSONDeserializable
19 |
20 | public init?(JSON: JSONDictionary) {
21 | if let username = JSON["username"] as? String {
22 | self.username = username
23 | } else {
24 | self.username = ""
25 | return nil
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/client/AresKit/CredentialStorage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CredentialStorage.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private let ServiceName = "com.indragie.ares"
12 | private let UserDefaultsUsernameKey = "username"
13 |
14 | public final class CredentialStorage {
15 | public static let sharedInstance = CredentialStorage()
16 | private let keychain: Keychain
17 |
18 | private init() {
19 | keychain = Keychain(service: ServiceName)
20 | }
21 |
22 | private var _activeToken: AccessToken?
23 | public var activeToken: AccessToken? {
24 | get {
25 | if let accessToken = _activeToken {
26 | return accessToken
27 | }
28 | let ud = NSUserDefaults.standardUserDefaults()
29 | if let username = ud.stringForKey(UserDefaultsUsernameKey),
30 | token = keychain[username] {
31 | let accessToken = AccessToken(username: username, token: token)
32 | _activeToken = accessToken
33 | return accessToken
34 | } else {
35 | return nil
36 | }
37 | }
38 | set {
39 | let ud = NSUserDefaults.standardUserDefaults()
40 | if let accessToken = newValue {
41 | ud.setObject(accessToken.username, forKey: UserDefaultsUsernameKey)
42 | keychain[accessToken.username] = accessToken.token
43 | _activeToken = accessToken
44 | } else {
45 | ud.removeObjectForKey(UserDefaultsUsernameKey)
46 | if let previousToken = _activeToken {
47 | do {
48 | try keychain.remove(previousToken.username)
49 | } catch let error {
50 | assertionFailure("Error removing credentials from keychain: \(error)")
51 | }
52 | }
53 | _activeToken = nil
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/client/AresKit/DeviceName.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DeviceName.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 | import UIKit
11 | #elseif os(OSX)
12 | import Cocoa
13 | #endif
14 |
15 | func getDeviceName() -> String {
16 | #if os(iOS)
17 | return UIDevice.currentDevice().name
18 | #elseif os(OSX)
19 | return NSHost.currentHost().localizedName ?? "Computer With No Name"
20 | #endif
21 | }
22 |
--------------------------------------------------------------------------------
/client/AresKit/FileTransferContext.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FileTransferContext.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/31/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private let ArchivedFilePathKey = "filePath";
12 |
13 | public struct FileTransferContext {
14 | public let filePath: String
15 |
16 | init(filePath: String) {
17 | self.filePath = filePath
18 | }
19 |
20 | init?(data: NSData) {
21 | if let dict = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [String: AnyObject],
22 | filePath = dict[ArchivedFilePathKey] as? String {
23 | self.filePath = filePath
24 | } else {
25 | return nil
26 | }
27 | }
28 |
29 | func archive() -> NSData {
30 | let dict: NSDictionary = [ArchivedFilePathKey: filePath]
31 | return NSKeyedArchiver.archivedDataWithRootObject(dict)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/client/AresKit/IncomingFileTransfer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IncomingFileTransfer.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/31/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MultipeerConnectivity
11 |
12 | public protocol IncomingFileTransferDelegate: AnyObject {
13 | func incomingFileTransfer(transfer: IncomingFileTransfer, didStartReceivingFileWithName name: String, progress: NSProgress)
14 | func incomingFileTransfer(transfer: IncomingFileTransfer, didReceiveFileWithName name: String, URL: NSURL)
15 | func incomingFileTransfer(transfer: IncomingFileTransfer, didFailToReceiveFileWithName name: String, error: NSError)
16 | }
17 |
18 | @objc public final class IncomingFileTransfer: NSObject, MCSessionDelegate {
19 | public let context: FileTransferContext
20 | let session: MCSession
21 | private let remotePeerID: MCPeerID
22 |
23 | public weak var delegate: IncomingFileTransferDelegate?
24 |
25 | init(context: FileTransferContext, localPeerID: MCPeerID, remotePeerID: MCPeerID) {
26 | self.context = context
27 | self.session = MCSession(peer: localPeerID)
28 | self.remotePeerID = remotePeerID
29 |
30 | super.init()
31 |
32 | self.session.delegate = self
33 | }
34 |
35 | // MARK: MCSessionDelegate
36 |
37 | public func session(session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, withProgress progress: NSProgress) {
38 | guard peerID == remotePeerID else { return }
39 |
40 | delegate?.incomingFileTransfer(self, didStartReceivingFileWithName: resourceName, progress: progress)
41 | }
42 |
43 | public func session(session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, atURL localURL: NSURL, withError error: NSError?) {
44 | guard peerID == remotePeerID else { return }
45 |
46 | if let error = error {
47 | delegate?.incomingFileTransfer(self, didFailToReceiveFileWithName: resourceName, error: error)
48 | } else {
49 | delegate?.incomingFileTransfer(self, didReceiveFileWithName: resourceName, URL: localURL)
50 | }
51 | session.disconnect()
52 | }
53 |
54 | public func session(session: MCSession, didReceiveCertificate certificate: [AnyObject]?, fromPeer peerID: MCPeerID, certificateHandler: (Bool) -> Void) {
55 | guard peerID == remotePeerID else { return }
56 | certificateHandler(true)
57 | }
58 |
59 | // Unused
60 |
61 | public func session(session: MCSession, didReceiveData data: NSData, fromPeer peerID: MCPeerID) {}
62 | public func session(session: MCSession, peer peerID: MCPeerID, didChangeState state: MCSessionState) {}
63 | public func session(session: MCSession, didReceiveStream stream: NSInputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
64 | }
65 |
--------------------------------------------------------------------------------
/client/AresKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2016 Indragie Karunaratne. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/client/AresKit/JSONDeserializable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSONDeserializable.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public typealias JSONDictionary = [String: AnyObject]
10 |
11 | public protocol JSONDeserializable {
12 | init?(JSON: JSONDictionary)
13 | }
14 |
--------------------------------------------------------------------------------
/client/AresKit/OutgoingFileTransfer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OutgoingFileTransfer.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/31/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MultipeerConnectivity
11 |
12 | public protocol OutgoingFileTransferDelegate: AnyObject {
13 | func outgoingFileTransfer(transfer: OutgoingFileTransfer, didStartWithProgress progress: NSProgress)
14 | func outgoingFileTransferDidComplete(transfer: OutgoingFileTransfer)
15 | func outgoingFileTransfer(transfer: OutgoingFileTransfer, didFailWithError error: NSError)
16 | }
17 |
18 | @objc public final class OutgoingFileTransfer: NSObject, MCSessionDelegate {
19 | public let context: FileTransferContext
20 | let session: MCSession
21 | private let remotePeerID: MCPeerID
22 |
23 | public weak var delegate: OutgoingFileTransferDelegate?
24 |
25 | init(context: FileTransferContext, localPeerID: MCPeerID, remotePeerID: MCPeerID) {
26 | self.context = context
27 | self.session = MCSession(peer: localPeerID)
28 | self.remotePeerID = remotePeerID
29 |
30 | super.init()
31 |
32 | self.session.delegate = self
33 | }
34 |
35 | // MARK: MCSessionDelegate
36 |
37 | public func session(session: MCSession, peer peerID: MCPeerID, didChangeState state: MCSessionState) {
38 | guard peerID == remotePeerID else { return }
39 | guard state == .Connected else { return }
40 |
41 | let URL = NSURL(fileURLWithPath: context.filePath)
42 | let name = (context.filePath as NSString).lastPathComponent
43 | let progress = session.sendResourceAtURL(URL, withName: name, toPeer: peerID) { error in
44 | if let error = error {
45 | self.delegate?.outgoingFileTransfer(self, didFailWithError: error)
46 | } else {
47 | self.delegate?.outgoingFileTransferDidComplete(self)
48 | }
49 | session.disconnect()
50 | }
51 | if let progress = progress {
52 | self.delegate?.outgoingFileTransfer(self, didStartWithProgress: progress)
53 | }
54 | }
55 |
56 | public func session(session: MCSession, didReceiveCertificate certificate: [AnyObject]?, fromPeer peerID: MCPeerID, certificateHandler: (Bool) -> Void) {
57 | guard peerID == remotePeerID else { return }
58 | certificateHandler(true)
59 | }
60 |
61 | // Unused
62 |
63 | public func session(session: MCSession, didReceiveData data: NSData, fromPeer peerID: MCPeerID) {}
64 | public func session(session: MCSession, didReceiveStream stream: NSInputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
65 | public func session(session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, withProgress progress: NSProgress) {}
66 | public func session(session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, atURL localURL: NSURL, withError error: NSError?) {}
67 | }
68 |
--------------------------------------------------------------------------------
/client/AresKit/PushNotification.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PushNotification.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/31/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public struct PushNotification: CustomStringConvertible {
10 | public let deviceUUID: String
11 | public let filePath: String
12 |
13 | public init?(payload: [NSObject: AnyObject]) {
14 | if let deviceUUID = payload["device_id"] as? String,
15 | filePath = payload["path"] as? String {
16 | self.deviceUUID = deviceUUID
17 | self.filePath = filePath
18 | } else {
19 | self.deviceUUID = ""
20 | self.filePath = ""
21 | return nil
22 | }
23 | }
24 |
25 | // MARK: CustomStringConvertible
26 |
27 | public var description: String {
28 | return "PushNotification{deviceUUID=\(deviceUUID), filePath=\(filePath)}"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/client/AresKit/RegisteredDevice.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RegisteredDevice.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public struct RegisteredDevice: CustomStringConvertible, JSONDeserializable {
10 | public let uuid: String
11 | public let deviceName: String
12 |
13 | // MARK: CustomStringConvertible
14 |
15 | public var description: String {
16 | return "RegisteredDevice{uuid=\(uuid), deviceName=\(deviceName)}"
17 | }
18 |
19 | // MARK: JSONDeserializable
20 |
21 | public init?(JSON: JSONDictionary) {
22 | if let uuid = JSON["uuid"] as? String,
23 | deviceName = JSON["device_name"] as? String {
24 | self.uuid = uuid
25 | self.deviceName = deviceName
26 | } else {
27 | self.uuid = ""
28 | self.deviceName = ""
29 | return nil
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/client/AresKit/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | public struct User: CustomStringConvertible {
10 | public let username: String
11 | public let password: String
12 |
13 | public init(username: String, password: String) {
14 | self.username = username
15 | self.password = password
16 | }
17 |
18 | // MARK: CustomStringConvertible
19 |
20 | public var description: String {
21 | return "User{username=\(username), password=\(password)}"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/client/Mac/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import AresKit
11 |
12 | @NSApplicationMain
13 | class AppDelegate: NSObject, NSApplicationDelegate, LoginWindowControllerDelegate {
14 |
15 | @IBOutlet weak var window: NSWindow!
16 | private var credentialStorage: CredentialStorage!
17 | private var client: Client!
18 | private var loginWindowController: LoginWindowController!
19 | private var statusItemController: StatusItemController!
20 |
21 | func applicationDidFinishLaunching(aNotification: NSNotification) {
22 | credentialStorage = CredentialStorage.sharedInstance
23 | client = Client()
24 |
25 | if let token = credentialStorage.activeToken {
26 | completeSetupWithToken(token)
27 | } else {
28 | showLoginWindowController()
29 | }
30 | }
31 |
32 | func showLoginWindowController() {
33 | loginWindowController = LoginWindowController(windowNibName: "LoginWindowController")
34 | loginWindowController.client = client
35 | loginWindowController.delegate = self
36 | loginWindowController.showWindow(nil)
37 | }
38 |
39 | func tearDownLoginWindowController() {
40 | loginWindowController.window?.orderOut(nil)
41 | loginWindowController = nil
42 | }
43 |
44 | func completeSetupWithToken(token: AccessToken) {
45 | statusItemController = StatusItemController(client: client, token: token)
46 | }
47 |
48 | // MARK: LoginWindowControllerDelegate
49 |
50 | func loginWindowController(controller: LoginWindowController, authenticatedWithToken token: AccessToken) {
51 | credentialStorage.activeToken = token
52 | tearDownLoginWindowController()
53 | completeSetupWithToken(token)
54 | }
55 |
56 | func loginWindowController(controller: LoginWindowController, failedToAuthenticateWithError error: NSError) {
57 | NSApp.presentError(error)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/green_orb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "green_orb.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "green_orb@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/green_orb.imageset/green_orb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/green_orb.imageset/green_orb.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/green_orb.imageset/green_orb@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/green_orb.imageset/green_orb@2x.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/red_orb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "red_orb.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "red_orb@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/red_orb.imageset/red_orb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/red_orb.imageset/red_orb.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/red_orb.imageset/red_orb@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/red_orb.imageset/red_orb@2x.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/rocket.imageset/777.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/rocket.imageset/777.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/rocket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "777.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/yellow_orb.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "yellow_orb.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "yellow_orb@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/yellow_orb.imageset/yellow_orb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/yellow_orb.imageset/yellow_orb.png
--------------------------------------------------------------------------------
/client/Mac/Assets.xcassets/yellow_orb.imageset/yellow_orb@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/Mac/Assets.xcassets/yellow_orb.imageset/yellow_orb@2x.png
--------------------------------------------------------------------------------
/client/Mac/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 | Default
536 |
537 |
538 |
539 |
540 |
541 |
542 | Left to Right
543 |
544 |
545 |
546 |
547 |
548 |
549 | Right to Left
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 | Default
561 |
562 |
563 |
564 |
565 |
566 |
567 | Left to Right
568 |
569 |
570 |
571 |
572 |
573 |
574 | Right to Left
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
--------------------------------------------------------------------------------
/client/Mac/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2016 Indragie Karunaratne. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 | NSAppTransportSecurity
34 |
35 | NSAllowsArbitraryLoads
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/client/Mac/LoginWindowController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginWindowController.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import AresKit
11 |
12 | protocol LoginWindowControllerDelegate: AnyObject {
13 | func loginWindowController(controller: LoginWindowController, failedToAuthenticateWithError error: NSError)
14 | func loginWindowController(controller: LoginWindowController, authenticatedWithToken token: AccessToken)
15 | }
16 |
17 | class LoginWindowController: NSWindowController {
18 | var client: Client?
19 | weak var delegate: LoginWindowControllerDelegate?
20 |
21 | @IBOutlet weak var usernameField: NSTextField!
22 | @IBOutlet weak var passwordField: NSSecureTextField!
23 |
24 | convenience init() {
25 | self.init(windowNibName: "LoginWindowController")
26 | }
27 |
28 | private func constructUser() -> User {
29 | return User(username: usernameField.stringValue, password: passwordField.stringValue)
30 | }
31 |
32 | @IBAction func login(sender: NSButton) {
33 | authenticate(constructUser())
34 | }
35 |
36 | @IBAction func register(sender: NSButton) {
37 | guard let client = client else { return }
38 | let user = constructUser()
39 | client.register(user) { result in
40 | switch result {
41 | case .Success:
42 | self.authenticate(user)
43 | case let .Failure(error):
44 | self.delegate?.loginWindowController(self, failedToAuthenticateWithError: error)
45 | }
46 | }
47 | }
48 |
49 | private func authenticate(user: User) {
50 | guard let client = client else { return }
51 | client.authenticate(user) { result in
52 | switch result {
53 | case let .Success(token):
54 | self.registerDevice(token)
55 | case let .Failure(error):
56 | self.delegate?.loginWindowController(self, failedToAuthenticateWithError: error)
57 | }
58 | }
59 | }
60 |
61 | private func registerDevice(token: AccessToken) {
62 | guard let client = client else { return }
63 | client.registerDevice(token) { result in
64 | switch result {
65 | case .Success:
66 | self.delegate?.loginWindowController(self, authenticatedWithToken: token)
67 | case let .Failure(error):
68 | self.delegate?.loginWindowController(self, failedToAuthenticateWithError: error)
69 | }
70 | }
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/client/Mac/LoginWindowController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | NSAllRomanInputSourcesLocaleIdentifier
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | DQ
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/client/Mac/StatusItemController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatusItemController.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import AresKit
11 |
12 | @objc final class StatusItemController: NSObject, ConnectionManagerDelegate, OutgoingFileTransferDelegate, NSWindowDelegate {
13 | let statusItem: NSStatusItem
14 | private let client: Client
15 | private let token: AccessToken
16 | private let connectionManager: ConnectionManager
17 |
18 | init(client: Client, token: AccessToken) {
19 | self.client = client
20 | self.token = token
21 |
22 | statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSSquareStatusItemLength)
23 | connectionManager = ConnectionManager(client: client, token: token)
24 |
25 | super.init()
26 |
27 | connectionManager.delegate = self
28 | connectionManager.outgoingFileTransferDelegate = self
29 | connectionManager.getDeviceList {
30 | self.connectionManager.startMonitoring()
31 | }
32 |
33 | if let button = statusItem.button {
34 | button.title = "🚀"
35 | if let window = button.window {
36 | window.registerForDraggedTypes([NSFilenamesPboardType])
37 | window.delegate = self
38 | }
39 | }
40 | }
41 |
42 | deinit {
43 | connectionManager.stopMonitoring()
44 | }
45 |
46 | // MARK: ConnectionManagerDelegate
47 |
48 | func connectionManager(manager: ConnectionManager, didUpdateDevices devices: [Device]) {
49 | let menu = NSMenu(title: "Devices")
50 | for device in devices {
51 | let registeredDevice = device.registeredDevice
52 | if registeredDevice.uuid == client.deviceUUID {
53 | continue
54 | }
55 | guard let item = menu.addItemWithTitle(registeredDevice.deviceName, action: "doNothing:", keyEquivalent: "") else { continue }
56 | item.target = self
57 | item.image = menuItemImageForDevice(device)
58 | }
59 | statusItem.menu = menu
60 | }
61 |
62 | @objc private func doNothing(sender: AnyObject) {}
63 |
64 | private func menuItemImageForDevice(device: Device) -> NSImage? {
65 | switch device.availability {
66 | case .Local:
67 | return NSImage(named: "green_orb")
68 | case .Remote:
69 | return NSImage(named: "yellow_orb")
70 | case .None:
71 | return NSImage(named: "red_orb")
72 | }
73 | }
74 |
75 | func connectionManager(manager: ConnectionManager, didFailWithError error: NSError) {
76 | print(error)
77 | }
78 |
79 | func connectionManager(manager: ConnectionManager, willBeginIncomingFileTransfer transfer: IncomingFileTransfer) {}
80 |
81 | func connectionManager(manager: ConnectionManager, willBeginOutgoingFileTransfer transfer: OutgoingFileTransfer) {
82 | print("Sending \(transfer.context.filePath)")
83 | }
84 |
85 | // MARK: OutgoingFileTransferDelegate
86 |
87 | func outgoingFileTransfer(transfer: OutgoingFileTransfer, didStartWithProgress progress: NSProgress) {
88 | let notification = NSUserNotification()
89 | notification.title = "File Transfer Started"
90 | notification.informativeText = "\(transfer.context.filePath)"
91 | NSUserNotificationCenter.defaultUserNotificationCenter().deliverNotification(notification)
92 | }
93 |
94 | func outgoingFileTransfer(transfer: OutgoingFileTransfer, didFailWithError error: NSError) {
95 | print("Sending \(transfer.context.filePath) failed: \(error)")
96 | }
97 |
98 | func outgoingFileTransferDidComplete(transfer: OutgoingFileTransfer) {
99 | let notification = NSUserNotification()
100 | notification.title = "File Transfer Complete"
101 | notification.informativeText = "\(transfer.context.filePath)"
102 | NSUserNotificationCenter.defaultUserNotificationCenter().deliverNotification(notification)
103 | }
104 |
105 | // MARK: Drag and Drop
106 |
107 | func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
108 | return .Copy
109 | }
110 |
111 | func performDragOperation(sender: NSDraggingInfo) -> Bool {
112 | let pasteboard = sender.draggingPasteboard()
113 | guard let types = pasteboard.types else { return false }
114 | if types.contains(NSFilenamesPboardType) {
115 | if let files = pasteboard.propertyListForType(NSFilenamesPboardType) as? [String],
116 | device = connectionManager.devices.first?.registeredDevice {
117 | for file in files {
118 | client.send(token, filePath: file, device: device) { result in
119 | if let error = result.error {
120 | print(error)
121 | }
122 | }
123 | }
124 | return true
125 | }
126 | }
127 | return false
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/client/iOS/APNSManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // APNSManager.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class APNSManager {
12 | var token: String?
13 | init() {}
14 | }
15 |
--------------------------------------------------------------------------------
/client/iOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Ares-iOS
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AresKit
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 | private var apnsManager: APNSManager!
15 | private var viewController: ViewController!
16 |
17 | var window: UIWindow?
18 |
19 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
20 | let settings = UIUserNotificationSettings(forTypes: [.Alert], categories: nil)
21 | UIApplication.sharedApplication().registerUserNotificationSettings(settings)
22 | UIApplication.sharedApplication().registerForRemoteNotifications()
23 |
24 | window = UIWindow(frame: UIScreen.mainScreen().bounds)
25 | window?.backgroundColor = .whiteColor()
26 |
27 | apnsManager = APNSManager()
28 | let client = Client()
29 | let credentialStorage = CredentialStorage.sharedInstance
30 | viewController = ViewController(client: client, credentialStorage: credentialStorage, apnsManager: apnsManager)
31 |
32 | window?.rootViewController = UINavigationController(rootViewController: viewController)
33 | window?.makeKeyAndVisible()
34 |
35 | if let payload = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? [NSObject: AnyObject],
36 | notification = PushNotification(payload: payload) {
37 | viewController.handlePushNotification(notification)
38 | }
39 |
40 | return true
41 | }
42 |
43 | func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
44 | apnsManager.token = deviceToken.hexadecimalString
45 | }
46 |
47 | func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
48 | print(error)
49 | }
50 |
51 | func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
52 | guard let notification = PushNotification(payload: userInfo) else { return }
53 | viewController.handlePushNotification(notification)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "placeholder.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "placeholder@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "placeholder@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/placeholder.imageset/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/iOS/Assets.xcassets/placeholder.imageset/placeholder.png
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.png
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.png
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/rocket.imageset/777.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/client/iOS/Assets.xcassets/rocket.imageset/777.png
--------------------------------------------------------------------------------
/client/iOS/Assets.xcassets/rocket.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "777.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/client/iOS/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/client/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | NSAppTransportSecurity
38 |
39 | NSAllowsArbitraryLoads
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/client/iOS/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AresKit
11 |
12 | protocol LoginViewControllerDelegate: AnyObject {
13 | func loginViewController(controller: LoginViewController, authenticatedWithToken token: AccessToken)
14 | }
15 |
16 | class LoginViewController: UIViewController {
17 | private let apnsManager: APNSManager
18 |
19 | var client: Client?
20 | weak var delegate: LoginViewControllerDelegate?
21 |
22 | @IBOutlet weak var usernameField: UITextField!
23 | @IBOutlet weak var passwordField: UITextField!
24 |
25 | init(apnsManager: APNSManager) {
26 | self.apnsManager = apnsManager
27 |
28 | super.init(nibName: nil, bundle: nil)
29 |
30 | title = "Login"
31 | }
32 |
33 | required init?(coder aDecoder: NSCoder) {
34 | fatalError("init(coder:) has not been implemented")
35 | }
36 |
37 | private func constructUser() -> User {
38 | return User(username: usernameField.text ?? "", password: passwordField.text ?? "")
39 | }
40 |
41 | @IBAction func login(sender: UIButton) {
42 | authenticate(constructUser())
43 | }
44 |
45 | @IBAction func register(sender: UIButton) {
46 | guard let client = client else { return }
47 | let user = constructUser()
48 | client.register(user) { result in
49 | switch result {
50 | case .Success:
51 | self.authenticate(user)
52 | case let .Failure(error):
53 | self.showAlertForError(error)
54 | }
55 | }
56 | }
57 |
58 | private func authenticate(user: User) {
59 | guard let client = client else { return }
60 | client.authenticate(user) { result in
61 | switch result {
62 | case let .Success(token):
63 | self.registerDevice(token)
64 | case let .Failure(error):
65 | self.showAlertForError(error)
66 | }
67 | }
68 | }
69 |
70 | private func registerDevice(token: AccessToken) {
71 | guard let client = client else { return }
72 | client.registerDevice(token, pushToken: apnsManager.token) { result in
73 | switch result {
74 | case .Success:
75 | self.delegate?.loginViewController(self, authenticatedWithToken: token)
76 | case let .Failure(error):
77 | self.showAlertForError(error)
78 | }
79 | }
80 | }
81 |
82 | private func showAlertForError(error: NSError) {
83 | let alertController = UIAlertController.alertControllerWithError(error)
84 | presentViewController(alertController, animated: true, completion: nil)
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/client/iOS/LoginViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/client/iOS/NSDataExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSDataExtensions.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSData {
12 | public var hexadecimalString: String {
13 | var bytes = [UInt8](count: length, repeatedValue: 0)
14 | getBytes(&bytes, length: length)
15 |
16 | let hexString = NSMutableString()
17 | for byte in bytes {
18 | hexString.appendFormat("%02x", UInt(byte))
19 | }
20 | return hexString as String
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/iOS/PreviewViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PreviewViewController.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/31/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import QuickLook
11 |
12 | class PreviewViewController: QLPreviewController, QLPreviewControllerDataSource {
13 | @objc private class PreviewItem: NSObject, QLPreviewItem {
14 | @objc let previewItemTitle: String?
15 | @objc let previewItemURL: NSURL
16 |
17 | init(previewItemTitle: String?, previewItemURL: NSURL) {
18 | self.previewItemTitle = previewItemTitle
19 | self.previewItemURL = previewItemURL
20 | }
21 | }
22 |
23 | private let previewItem: PreviewItem
24 |
25 | init(fileName: String, URL: NSURL) {
26 | previewItem = PreviewItem(previewItemTitle: fileName, previewItemURL: URL)
27 | super.init(nibName: nil, bundle: nil)
28 | dataSource = self
29 | }
30 |
31 | required init?(coder aDecoder: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 |
35 | // MARK: QLPreviewControllerDataSource
36 |
37 | func numberOfPreviewItemsInPreviewController(controller: QLPreviewController) -> Int {
38 | return 1
39 | }
40 |
41 | func previewController(controller: QLPreviewController, previewItemAtIndex index: Int) -> QLPreviewItem {
42 | return previewItem
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/client/iOS/UIAlertControllerExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertControllerExtensions.swift
3 | // Ares
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIAlertController {
12 | static func alertControllerWithError(error: NSError) -> UIAlertController {
13 | let alert = UIAlertController(title: error.localizedDescription, message: error.localizedRecoverySuggestion, preferredStyle: .Alert)
14 | alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .Default, handler: nil))
15 | return alert
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/client/iOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Ares-iOS
4 | //
5 | // Created by Indragie on 1/30/16.
6 | // Copyright © 2016 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AresKit
11 | import KVOController
12 | import QuickLook
13 |
14 | class ViewController: UIViewController, LoginViewControllerDelegate, ConnectionManagerDelegate, IncomingFileTransferDelegate, QLPreviewControllerDelegate {
15 | private enum State {
16 | case Default
17 | case WaitingForDiscovery
18 | case Transferring
19 | }
20 |
21 | private let credentialStorage: CredentialStorage
22 | private let apnsManager: APNSManager
23 | private let client: Client
24 | private var _KVOController: FBKVOController!
25 |
26 | private var connectionManager: ConnectionManager?
27 | private var queuedPushNotifications = [PushNotification]()
28 | private var temporaryFileURL: NSURL?
29 |
30 | private var state = State.Default {
31 | didSet {
32 | loadViewIfNeeded()
33 | switch state {
34 | case .Default:
35 | placeholderImageView.hidden = false
36 | determinateProgressStackView.hidden = true
37 | indeterminateProgressStackView.hidden = true
38 | activityIndicator.stopAnimating()
39 | case .WaitingForDiscovery:
40 | placeholderImageView.hidden = true
41 | determinateProgressStackView.hidden = true
42 | indeterminateProgressStackView.hidden = false
43 | activityIndicator.startAnimating()
44 | case .Transferring:
45 | placeholderImageView.hidden = true
46 | determinateProgressStackView.hidden = false
47 | indeterminateProgressStackView.hidden = true
48 | activityIndicator.stopAnimating()
49 | }
50 | }
51 | }
52 |
53 | @IBOutlet weak var progressView: UIProgressView!
54 | @IBOutlet weak var progressLabel: UILabel!
55 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
56 | @IBOutlet weak var determinateProgressStackView: UIStackView!
57 | @IBOutlet weak var indeterminateProgressStackView: UIStackView!
58 | @IBOutlet weak var placeholderImageView: UIImageView!
59 |
60 | init(client: Client, credentialStorage: CredentialStorage, apnsManager: APNSManager) {
61 | self.client = client
62 | self.credentialStorage = credentialStorage
63 | self.apnsManager = apnsManager
64 |
65 | super.init(nibName: nil, bundle: nil)
66 |
67 | self._KVOController = FBKVOController(observer: self)
68 | title = "🚀 Ares"
69 | }
70 |
71 | required init?(coder aDecoder: NSCoder) {
72 | fatalError("init(coder:) has not been implemented")
73 | }
74 |
75 | override func viewDidLoad() {
76 | super.viewDidLoad()
77 | if let token = credentialStorage.activeToken {
78 | completeSetupWithToken(token)
79 | } else {
80 | dispatch_async(dispatch_get_main_queue()) {
81 | self.presentLoginViewController()
82 | }
83 | }
84 | }
85 |
86 | // MARK: Login
87 |
88 | private func presentLoginViewController() {
89 | let loginViewController = LoginViewController(apnsManager: apnsManager)
90 | loginViewController.client = client
91 | loginViewController.delegate = self
92 |
93 | let navigationController = UINavigationController(rootViewController: loginViewController)
94 | presentViewController(navigationController, animated: true, completion: nil)
95 | }
96 |
97 | private func completeSetupWithToken(token: AccessToken) {
98 | let connectionManager = ConnectionManager(client: client, token: token)
99 | connectionManager.delegate = self
100 | connectionManager.incomingFileTransferDelegate = self
101 | connectionManager.getDeviceList {
102 | connectionManager.startMonitoring()
103 |
104 | self.queuedPushNotifications.forEach(connectionManager.queueNotification)
105 | self.queuedPushNotifications.removeAll()
106 | }
107 | self.connectionManager = connectionManager
108 | }
109 |
110 | // MARK: Notification Handling
111 |
112 | func handlePushNotification(notification: PushNotification) {
113 | state = .WaitingForDiscovery
114 | if let connectionManager = connectionManager {
115 | connectionManager.queueNotification(notification)
116 | } else {
117 | queuedPushNotifications.append(notification)
118 | }
119 | }
120 |
121 | // MARK: LoginViewControllerDelegae
122 |
123 | func loginViewController(controller: LoginViewController, authenticatedWithToken token: AccessToken) {
124 | credentialStorage.activeToken = token
125 | completeSetupWithToken(token)
126 |
127 | dismissViewControllerAnimated(true, completion: nil)
128 | }
129 |
130 | // MARK: ConnectionManagerDelegate
131 |
132 | func connectionManager(manager: ConnectionManager, willBeginOutgoingFileTransfer transfer: OutgoingFileTransfer) {}
133 |
134 | func connectionManager(manager: ConnectionManager, willBeginIncomingFileTransfer transfer: IncomingFileTransfer) {
135 | print("Receiving \(transfer.context.filePath)")
136 | }
137 |
138 | func connectionManager(manager: ConnectionManager, didFailWithError error: NSError) {
139 | print(error)
140 | }
141 |
142 | func connectionManager(manager: ConnectionManager, didUpdateDevices devices: [Device]) {}
143 |
144 | // MARK: IncomingFileTransferDelegate
145 |
146 | func incomingFileTransfer(transfer: IncomingFileTransfer, didStartReceivingFileWithName name: String, progress: NSProgress) {
147 | let fileName = (transfer.context.filePath as NSString).lastPathComponent
148 |
149 | dispatch_async(dispatch_get_main_queue()) {
150 | self.progressLabel.text = "Receiving \(fileName)..."
151 | self.state = .Transferring
152 | }
153 |
154 | _KVOController.observe(progress, keyPath: "fractionCompleted", options: []) { (_, _, _) in
155 | dispatch_async(dispatch_get_main_queue()) {
156 | self.progressView.progress = Float(progress.fractionCompleted)
157 | }
158 | }
159 | }
160 |
161 | func incomingFileTransfer(transfer: IncomingFileTransfer, didFailToReceiveFileWithName name: String, error: NSError) {
162 | print("Failed to receive \(name): \(error)")
163 | }
164 |
165 | func incomingFileTransfer(transfer: IncomingFileTransfer, didReceiveFileWithName name: String, URL: NSURL) {
166 | if let _ = presentedViewController {
167 | dismissViewControllerAnimated(true) {
168 | self.showPreviewControllerForFileName(name, URL: URL)
169 | }
170 | } else {
171 | showPreviewControllerForFileName(name, URL: URL)
172 | }
173 | }
174 |
175 | private func showPreviewControllerForFileName(name: String, URL: NSURL) {
176 | guard let directoryURL = URL.URLByDeletingLastPathComponent else { return }
177 | let fixedURL = directoryURL.URLByAppendingPathComponent(name)
178 | temporaryFileURL = fixedURL
179 |
180 | let fm = NSFileManager.defaultManager()
181 | do { try fm.removeItemAtURL(fixedURL) } catch _ {}
182 | do {
183 | try fm.moveItemAtURL(URL, toURL: fixedURL)
184 | } catch let error {
185 | fatalError("Error moving \(URL) to \(fixedURL): \(error)")
186 | }
187 |
188 | dispatch_async(dispatch_get_main_queue()) {
189 | let previewController = PreviewViewController(fileName: name, URL: fixedURL)
190 | previewController.delegate = self
191 | self.presentViewController(previewController, animated: true) {
192 | self.state = .Default
193 | }
194 | }
195 | }
196 |
197 | // MARK: QLPreviewControllerDelegate
198 |
199 | func previewControllerDidDismiss(controller: QLPreviewController) {
200 | guard let fileURL = temporaryFileURL else { return }
201 | do {
202 | try NSFileManager.defaultManager().removeItemAtURL(fileURL)
203 | } catch _ {}
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/client/iOS/ViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
--------------------------------------------------------------------------------
/server/Procfile:
--------------------------------------------------------------------------------
1 | web: node index.js
2 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 | var bodyParser = require('body-parser');
4 | var MongoClient = require('mongodb').MongoClient;
5 | var assert = require('assert');
6 | var async = require('async');
7 | var bcrypt = require('bcrypt');
8 | var jwt = require('jsonwebtoken');
9 | var apn = require('apn');
10 | var path = require('path');
11 |
12 | // ######## CONSTANTS #########
13 |
14 | USERS_COLLECTION = 'users';
15 | DEVICES_COLLECTION = 'devices';
16 | TOKEN_EXPIRY_SECONDS = 86400; // 24 hours
17 | ERROR_USER_EXISTS = new Error('USER_EXISTS');
18 | ERROR_USER_DOES_NOT_EXIST = new Error('USER_DOES_NOT_EXIST');
19 | ERROR_DEVICE_DOES_NOT_EXIST = new Error('DEVICE_DOES_NOT_EXIST');
20 | ERROR_PASSWORD_INCORRECT = new Error('PASSWORD_INCORRECT');
21 | ERROR_INVALID_TOKEN = new Error('INVALID_TOKEN');
22 |
23 | // ######## EXPRESS #########
24 |
25 | app.set('port', (process.env.PORT || 5000));
26 | app.set('mongo_uri', process.env.MONGOLAB_URI);
27 | app.set('secret', process.env.APP_SECRET);
28 |
29 | app.use(bodyParser.urlencoded({ extended: false }));
30 |
31 | app.get('/', function (req, res) {
32 | res.send('Hello World!');
33 | });
34 |
35 | app.listen(app.get('port'), function () {
36 | console.log('Example server started');
37 | });
38 |
39 | app.post('/register', function(req, res, next) {
40 | async.waterfall([
41 | connectMongoDB,
42 | async.apply(createUser, req.body.username, req.body.password)
43 | ], function(err, result) {
44 | if (err) {
45 | return next(err);
46 | } else {
47 | res.json({
48 | success: true,
49 | result: { username: result.ops[0].username }
50 | });
51 | }
52 | });
53 | });
54 |
55 | app.post('/authenticate', function(req, res, next) {
56 | var username = req.body.username;
57 |
58 | async.waterfall([
59 | connectMongoDB,
60 | async.apply(getUser, username),
61 | function(user, callback) {
62 | if (user) {
63 | callback(null, user);
64 | } else {
65 | callback(ERROR_USER_DOES_NOT_EXIST);
66 | }
67 | },
68 | function(user, callback) {
69 | verifyPassword(user.password_hash, req.body.password, function(err, res) {
70 | if (err) {
71 | callback(err);
72 | } else {
73 | callback(null, res, user);
74 | }
75 | });
76 | },
77 | function(verified, user, callback) {
78 | if (verified) {
79 | callback(null, user);
80 | } else {
81 | callback(ERROR_PASSWORD_INCORRECT);
82 | }
83 | },
84 | ], function(err, user) {
85 | if (err) {
86 | return next(err);
87 | } else {
88 | var token = jwt.sign(user, app.get('secret'), {
89 | expiresIn: TOKEN_EXPIRY_SECONDS
90 | });
91 | res.json({
92 | success: true,
93 | result: {
94 | username: username,
95 | token: token
96 | }
97 | });
98 | }
99 | });
100 | });
101 |
102 | app.use(function(req, res, next) {
103 | var token = req.body.token || req.query.token || req.headers['x-access-token'];
104 | if (token) {
105 | jwt.verify(token, app.get('secret'), function(err, user) {
106 | if (err) {
107 | next(ERROR_INVALID_TOKEN);
108 | } else {
109 | req.user = user;
110 | next();
111 | }
112 | });
113 | } else {
114 | res.status(403).json({
115 | success: false,
116 | error: 'No authentication token provided'
117 | });
118 | }
119 | });
120 |
121 | app.post('/register_device', function(req, res, next) {
122 | var userID = req.user._id;
123 | var uuid = req.body.uuid;
124 | var deviceName = req.body.device_name;
125 | var pushToken = req.body.push_token;
126 | async.waterfall([
127 | connectMongoDB,
128 | async.apply(registerDevice, userID, uuid, deviceName, pushToken)
129 | ], function(err, result) {
130 | if (err) {
131 | return next(err);
132 | } else {
133 | res.json({
134 | success: true,
135 | result: {
136 | uuid: uuid,
137 | device_name: deviceName
138 | }
139 | });
140 | }
141 | });
142 | });
143 |
144 | app.get('/devices', function(req, res, next) {
145 | async.waterfall([
146 | connectMongoDB,
147 | async.apply(getDevices, req.user._id)
148 | ], function(err, result) {
149 | if (err) {
150 | return next(err);
151 | } else {
152 | res.json({
153 | success: true,
154 | result: result
155 | });
156 | }
157 | });
158 | });
159 |
160 |
161 | var apnConnection = new apn.Connection({ production: false });
162 |
163 | app.post('/send', function(req, res, next) {
164 | async.waterfall([
165 | connectMongoDB,
166 | async.apply(getDevice, req.user._id, req.body.to_id),
167 | function(device, callback) {
168 | if (device) {
169 | var apnsDevice = new apn.Device(device.push_token);
170 | var notification = new apn.Notification();
171 | var filePath = req.body.file_path;
172 | notification.alert = path.basename(filePath);
173 | notification.payload = { 'device_id': req.body.from_id, 'path': filePath };
174 | apnConnection.pushNotification(notification, apnsDevice);
175 | callback(null, {});
176 | } else {
177 | callback(ERROR_DEVICE_DOES_NOT_EXIST);
178 | }
179 | }
180 | ], function(err, result) {
181 | if (err) {
182 | return next(err);
183 | } else {
184 | res.json({
185 | success: true,
186 | result: result
187 | });
188 | }
189 | });
190 | });
191 |
192 | app.use(function(err, req, res, next) {
193 | res.status(400).json({
194 | success: false,
195 | error: err.message
196 | });
197 | });
198 |
199 | // ######## MONGODB #########
200 |
201 | var connectMongoDB = function(callback) {
202 | MongoClient.connect(app.get('mongo_uri'), callback);
203 | };
204 |
205 | var getUser = function(username, db, callback) {
206 | var users = db.collection(USERS_COLLECTION);
207 | users.findOne({ username: username }, callback);
208 | };
209 |
210 | var createUser = function(username, password, db, finalCallback) {
211 | async.waterfall([
212 | async.apply(getUser, username, db),
213 | function(user, callback) {
214 | if (user) {
215 | callback(ERROR_USER_EXISTS);
216 | } else {
217 | callback(null, password);
218 | }
219 | },
220 | hashPassword,
221 | function(hash, callback) {
222 | db.collection(USERS_COLLECTION).insert({
223 | username: username,
224 | password_hash: hash
225 | }, callback);
226 | }
227 | ], finalCallback);
228 | };
229 |
230 | var registerDevice = function(userID, uuid, deviceName, pushToken, db, callback) {
231 | db.collection(DEVICES_COLLECTION).insert({
232 | user_id: userID,
233 | _id: uuid,
234 | device_name: deviceName,
235 | push_token: pushToken
236 | }, callback);
237 | };
238 |
239 | var getDevices = function(userID, db, callback) {
240 | var devices = db.collection(DEVICES_COLLECTION);
241 | devices.find({ user_id: userID }).map(function(device) {
242 | return {
243 | uuid: device._id,
244 | device_name: device.device_name
245 | };
246 | }).toArray(callback);
247 | };
248 |
249 | var getDevice = function(userID, deviceID, db, callback) {
250 | db.collection(DEVICES_COLLECTION).findOne({
251 | user_id: userID,
252 | _id: deviceID
253 | }, callback);
254 | };
255 |
256 | // ######## HASHING #########
257 |
258 | var hashPassword = function(password, callback) {
259 | bcrypt.genSalt(10, function(err, salt) {
260 | bcrypt.hash(password, salt, callback);
261 | });
262 | };
263 |
264 | var verifyPassword = function(hash, password, callback) {
265 | bcrypt.compare(password, hash, callback);
266 | };
267 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ares-server",
3 | "version": "1.0.0",
4 | "description": "Back-end for the Ares file transfer service",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/indragiek/Ares.git"
12 | },
13 | "keywords": [
14 | "ios",
15 | "mac",
16 | "file",
17 | "transfer",
18 | "bluetooth",
19 | "wifi"
20 | ],
21 | "author": "Indragie Karunaratne",
22 | "license": "UNLICENSED",
23 | "bugs": {
24 | "url": "https://github.com/indragiek/Ares/issues"
25 | },
26 | "homepage": "https://github.com/indragiek/Ares#readme",
27 | "dependencies": {
28 | "apn": "^1.7.5",
29 | "async": "^1.5.2",
30 | "bcrypt": "^0.8.5",
31 | "body-parser": "^1.14.2",
32 | "express": "^4.13.4",
33 | "jsonwebtoken": "^5.5.4",
34 | "kerberos": "~0.0.17",
35 | "mongodb": "^2.1.4",
36 | "ws": "^1.0.1"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/steps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/indragiek/Ares/bf09f2e124473a32b8e6dfc88d0b08dea0c48e8e/steps.png
--------------------------------------------------------------------------------