| // Copyright 2016 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| import Foundation |
| |
| /// The singleton instance of Syncbase that represents the local store. |
| public enum Syncbase { |
| /// The dispatch queue to run callbacks on. Defaults to main. |
| public static var queue: dispatch_queue_t = dispatch_get_main_queue() |
| // Internal variables |
| static var didInit = false |
| static var isUnitTest = false |
| |
| public static func configure( |
| // Default to Application Support/Syncbase. |
| rootDir rootDir: String = NSFileManager.defaultManager() |
| .URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)[0] |
| .URLByAppendingPathComponent("Syncbase") |
| .path!, |
| queue: dispatch_queue_t = dispatch_get_main_queue()) throws { |
| if didInit { |
| throw SyncbaseError.AlreadyConfigured |
| } |
| if rootDir == "" { |
| throw SyncbaseError.IllegalArgument(detail: "Missing rootDir") |
| } |
| if !NSFileManager.defaultManager().fileExistsAtPath(rootDir) { |
| try NSFileManager.defaultManager().createDirectoryAtPath( |
| rootDir, |
| withIntermediateDirectories: true, |
| attributes: [NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication]) |
| } |
| v23_syncbase_Init( |
| v23_syncbase_Bool(false), |
| try rootDir.toCgoString(), |
| v23_syncbase_Bool(isUnitTest)) |
| if isLoggedIn { |
| try serve() |
| } |
| Syncbase.queue = queue |
| Syncbase.didInit = true |
| } |
| |
| /// Starts serving Syncbase post-initialization and login. Internal use only. |
| static func serve() throws { |
| try VError.maybeThrow { errPtr in |
| v23_syncbase_Serve(errPtr) |
| } |
| } |
| |
| /// Shuts down the Syncbase service. You must call configure again before any calls will work. |
| public static func shutdown() { |
| v23_syncbase_Shutdown() |
| Syncbase.didInit = false |
| } |
| |
| /// Create a database using the relative name and user's blessings. |
| public static func database(name: String) throws -> Database { |
| if !Syncbase.didInit { |
| throw SyncbaseError.NotConfigured |
| } |
| return try database(Identifier(name: name, blessing: try Principal.appBlessing())) |
| } |
| |
| /// DatabaseForId returns the Database with the given app blessing and name (from the Id struct). |
| public static func database(databaseId: Identifier) throws -> Database { |
| if !Syncbase.didInit { |
| throw SyncbaseError.NotConfigured |
| } |
| return try Database(databaseId: databaseId, batchHandle: nil) |
| } |
| |
| /// ListDatabases returns a list of all Database ids that the caller is allowed to see. |
| /// The list is sorted by blessing, then by name. |
| public static func listDatabases() throws -> [Identifier] { |
| if !Syncbase.didInit { |
| throw SyncbaseError.NotConfigured |
| } |
| var ids = v23_syncbase_Ids() |
| return try VError.maybeThrow { err in |
| v23_syncbase_ServiceListDatabases(&ids, err) |
| return ids.toIdentifiers() |
| } |
| } |
| |
| /// Must return true before any Syncbase operation can work. Authorize using GoogleCredentials |
| /// created from a Google OAuth token (you should use the Google Sign In SDK to get this). |
| public static var isLoggedIn: Bool { |
| var ret = v23_syncbase_Bool(false) |
| v23_syncbase_IsLoggedIn(&ret) |
| return ret.toBool() |
| } |
| |
| /// For debugging the current Syncbase user blessings. |
| public static var loggedInBlessingDebugDescription: String { |
| return Principal.blessingsDebugDescription |
| } |
| |
| public typealias LoginCallback = (err: ErrorType?) -> Void |
| |
| /// Authorize using an oauth token. Right now only Google OAuth token is supported |
| /// (you should use the Google Sign In SDK to get this), and you should use the |
| /// GoogleOAuthCredentials struct to pass the token. |
| /// |
| /// You must login and have valid credentials before any Syncbase operation will work. |
| /// |
| /// Calls `callback` on `Syncbase.queue` with any error that occured, or nil on success. |
| /// |
| /// TODO(zinman): Make sure the blessings cache works so we don't actually have to login |
| /// every single time. |
| public static func login(credentials: OAuthCredentials, callback: LoginCallback) { |
| if !Syncbase.didInit { |
| callback(err: SyncbaseError.NotConfigured) |
| } |
| // Go's login is blocking, so call on a background concurrent queue. |
| dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) { |
| var err: ErrorType? = nil |
| do { |
| try VError.maybeThrow { errPtr in |
| v23_syncbase_Login( |
| try credentials.provider.rawValue.toCgoString(), |
| try credentials.token.toCgoString(), |
| errPtr) |
| } |
| } catch (let e) { |
| err = e |
| } |
| dispatch_async(Syncbase.queue) { |
| callback(err: err) |
| } |
| } |
| } |
| |
| public static func getPermissions() throws -> (Permissions, PermissionsVersion) { |
| if !Syncbase.didInit { |
| throw SyncbaseError.NotConfigured |
| } |
| var cPermissions = v23_syncbase_Permissions() |
| var cVersion = v23_syncbase_String() |
| try VError.maybeThrow { errPtr in |
| v23_syncbase_ServiceGetPermissions( |
| &cPermissions, |
| &cVersion, |
| errPtr) |
| } |
| // TODO(zinman): Verify that permissions defaulting to zero-value is correct for Permissions. |
| // We force cast of cVersion because we know it can be UTF-8 converted. |
| return (try cPermissions.toPermissions() ?? Permissions(), cVersion.toString()!) |
| } |
| |
| public static func setPermissions(permissions: Permissions, version: PermissionsVersion) throws { |
| if !Syncbase.didInit { |
| throw SyncbaseError.NotConfigured |
| } |
| try VError.maybeThrow { errPtr in |
| v23_syncbase_ServiceSetPermissions( |
| try v23_syncbase_Permissions(permissions), |
| try version.toCgoString(), |
| errPtr) |
| } |
| } |
| } |