Jan 15, 2015
Clemens Auer
REST ing with the new Yandex.Disk API
Developer
What Is Yandex Disk?
- 26 Million registered Disk users.
- 6 Billion files.
- 14 Million files added per day.
What Is Yandex Disk?
How much storage space users get for free?
- Basic: 10 GB.
- Invites: up to 10 GB.
- Partner offers: currently up to 50 GB.
What Is Yandex Disk?
For those who need more:
What Is Yandex Disk?
Storage 10 GB 100 GB 1 TB
per month 30 RUB 150 RUB 900 RUB
per year 300 RUB 1500 RUB 9000 RUB
Clients
- Web: disk.yandex.ru, widgets.
- Desktop: Windows, Mac, Linux
- Mobile: iOS, Android, WinPhone
- 3rd party
What Is Yandex Disk?
3rd party clients.
- Official SDKs (JAVA, Obj-C, C#)
- WebDAV + extensions
- REST API
What Is Yandex Disk?
WebDAV vs. REST API
WebDAV vs. REST API
WebDAV RESTUpload ✓ ✓
Download ✓ ✓Create Folder ✓ ✓
Copy ✓ ✓Move ✓ ✓Delete ✓ ✓
List ✓ ✓
Support for public files
WebDAV vs. REST API
WebDAV RESTShare ✓ ✓
Unshare ✓ ✓List Metadata ✗ ✓
Download ✗ ✓Save to Disk ✗ ✓
Working with the trash
WebDAV vs. REST API
WebDAV RESTList ✗ ✓
Delete ✗ ✓Restore ✗ ✓
What is missing?
WebDAV vs. REST API
WebDAV RESTBasic Auth ✓ ✗
Free/Used Space ✓ ✗*Hash Upload ✓ ✗
Custom Props ✓ ✗
Userinfo ✓ ✗
- Register application
- Register dev accounts
- Get token for development
- Полигон
- Code
Getting Started
Register application
Register application
Register application
Register application
Register application
Register dev accounts.
Get token for development
https://oauth.yandex.ru/authorize?response_type=token&client_id=
Get token for development
https://oauth.yandex.ru/verification_code?dev=True# access_token=d8edc4f3a698473fbc87634c41b2ca81 &token_type=bearer &state= &expires_in=31536000
Полигон
- Register application
- Register dev accounts
- Get token for development
- Полигон
- Code
Getting Started
import Foundation !public class Disk { … } !let disk = Disk(token: "d8edc4f3a698473fbc87634c41b2ca81") var fileURL : NSURL !disk.uploadURL(fileURL, toPath: fileURL.lastPathComponent, overwrite: true) { // handle errors } !disk.deletePath(fileURL.lastPathComponent, permanently: false) { // handle response }
Code example
What we try to achieve:
public class Disk { public let token : String public let baseURL = "https://cloud-api.yandex.net:443" ! var additionalHTTPHeaders : [String:String] { return [ "Accept" : "application/json", "Authorization" : "OAuth \(token)", "User-Agent" : "Mobile Camp Demo" ] } }
Code example
Basic properties in Disk class
public class Disk { public let token : String public let baseURL = "https://cloud-api.yandex.net:443" ! var additionalHTTPHeaders : [String:String] { return [ "Accept" : "application/json", "Authorization" : "OAuth \(token)", "User-Agent" : "Mobile Camp Demo" ] } ! public lazy var session : NSURLSession = { let config = NSURLSessionConfiguration.defaultSessionConfiguration() config.HTTPAdditionalHeaders = self.additionalHTTPHeaders ! return NSURLSession(configuration: config) }() ! public init(token:String) { self.token = token } }
Code example
Complete basic Disk class
extension Disk { class func JSONDictionaryWithData(data:NSData!, onError:(NSError!)->Void) -> NSDictionary? { ! var error: NSError? let root = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? NSDictionary ! if root == nil { onError(NSError(…)) return nil } ! return root } }
Code example
Disk extension for de-serializing JSON data
extension NSURLSession { func jsonTaskWithMethod(method:String, url: NSURL!, onError: ((NSError!) -> Void)!, onCompletion: ((NSDictionary, NSHTTPURLResponse) -> Void)!) -> NSURLSessionDataTask! { ! let request = NSMutableURLRequest(URL: url) request.HTTPMethod = method ! return dataTaskWithRequest(request) { (data, response, error) -> Void in ! let jsonRoot = Disk.JSONDictionaryWithData(data, onError: onError) ! if let jsonRoot = jsonRoot { switch response.statusCode { case 400...599: return onError(…) default: return onCompletion(jsonRoot, response) } } else { return // handler already called from JSONDictionaryWithData } } } }
Code example
NSURLSessing extension for doing JSON requests
Take a peek at the documentation
extension Disk { class func hrefMethodTemplatedWithDictionary(dict:NSDictionary?) -> (href:String, method:String, templated:Bool) { var href = "" var method = "" var templated = false ! if let json = dict { if let str = json["href"] as? String { href = str } if let str = json["method"] as? String { method = str } if let nr = json["templated"] as? NSNumber { templated = nr.boolValue } } ! return (href, method, templated) } }
Code example
Disk extension for processing common JSON object
extension Disk { public func uploadURL(fileURL:NSURL, toPath path:String, overwrite:Bool?, handler:(NSError!) -> Void) -> Void { ! var url = "\(baseURL)/v1/disk/resources/upload?path=\(path.urlEncoded())" ! if let overwrite = overwrite { url += "&overwrite=\(overwrite)" } let error = { handler($0) } ! session.jsonTaskWithMethod("GET", url: NSURL(string: url), onError: error) { (jsonRoot, response)->Void in ! let (href, method, templated) = Disk.hrefMethodTemplatedWithDictionary(jsonRoot) ! let request = NSMutableURLRequest(URL: NSURL(string: href)) request.HTTPMethod = method ! self.session.uploadTaskWithRequest(request, fromFile: fileURL) { (data, response, trasferError)->Void in ! return error(trasferError) }.resume() }.resume() } }
Code example
Disk extension implementing file upload
Take another peek at the documentation
public enum DeletionResult { case Done case InProcess(href:String, method:String, templated:Bool) case Failed(NSError!) } !extension Disk { public func deletePath(path:String, permanently:Bool?, handler:(result:DeletionResult) -> Void) -> Void { var url = "\(baseURL)/v1/disk/resources?path=\(path.urlEncoded())" ! if let permanently = permanently { url += "&permanently=\(permanently)" } let error = { handler(result: .Failed($0)) } ! session.jsonTaskWithMethod("DELETE", url: NSURL(string: url), onError: error) { (jsonRoot, response)->Void in switch response.statusCode { case 202: return handler(result: .InProcess(Disk.hrefMethodTemplatedWithDictionary(jsonRoot))) case 204: return handler(result: .Done) default: return error(NSError(…)) } }.resume() } }
Code example
Disk extension implementing delete.
Swift
- enum, switch, closures, tuples
- Type safety vs. Obj-C weak typing
- Volatile: Xcode release notes.
- Apple Dev Forums
Lessons Learned
REST API
- Documentation vs. Полигон
- Fast
- OAuth
- Roadmap ahead
Lessons Learned
In general
- Test driven development saves time.
- Autotest the API version
- Report bugs
http://feedback2.yandex.ru/disk/api/
Lessons Learned
https://oauth.yandex.ru
https://oauth.yandex.ru/client/new
http://api.yandex.ru/disk/
http://api.yandex.ru/login/
https://yadi.sk/d/5b9BoUjtZvJhp
References