Repository: Azure/azure-mobile-services Branch: master Commit: 1198c8576bf1 Files: 1814 Total size: 17.4 MB Directory structure: gitextract_8ud35due/ ├── .gitattributes ├── .gitignore ├── .gitmodules ├── CHANGELOG.android.md ├── CHANGELOG.ios.md ├── CHANGELOG.javascript.md ├── CHANGELOG.managed.md ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── component/ │ ├── .gitignore │ ├── Details.md │ ├── GettingStarted.md │ ├── License.md │ ├── component.yaml │ ├── icon.psd │ └── samples/ │ ├── androidsample/ │ │ ├── androidsample/ │ │ │ ├── Assets/ │ │ │ │ └── AboutAssets.txt │ │ │ ├── Properties/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ ├── AboutResources.txt │ │ │ │ ├── Resource.designer.cs │ │ │ │ ├── layout/ │ │ │ │ │ ├── Activity_To_Do.axml │ │ │ │ │ └── Row_List_To_Do.axml │ │ │ │ ├── menu/ │ │ │ │ │ └── activity_main.xml │ │ │ │ ├── values/ │ │ │ │ │ ├── Strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── values-v11/ │ │ │ │ │ └── styles.xml │ │ │ │ └── values-v14/ │ │ │ │ └── styles.xml │ │ │ ├── ToDoActivity.cs │ │ │ ├── ToDoItem.cs │ │ │ ├── ToDoItemAdapter.cs │ │ │ ├── androidsample.csproj │ │ │ └── app.config │ │ └── androidsample.sln │ └── iOSsample/ │ ├── iOSsample/ │ │ ├── AppDelegate.cs │ │ ├── Info.plist │ │ ├── Main.cs │ │ ├── MainStoryboard_iPad.storyboard │ │ ├── MainStoryboard_iPhone.storyboard │ │ ├── QSTodoListViewController.cs │ │ ├── QSTodoListViewController.designer.cs │ │ ├── QSTodoService.cs │ │ ├── ToDoItem.cs │ │ ├── app.config │ │ └── iOSsample.csproj │ └── iOSsample.sln ├── docs/ │ ├── README.md │ ├── mobile-services-android-get-started-data.md │ ├── mobile-services-android-get-started-offline-data.md │ ├── mobile-services-android-get-started-users.md │ ├── mobile-services-android-get-started.md │ ├── mobile-services-android-how-to-use-client-library.md │ ├── mobile-services-android-upload-data-blob-storage.md │ ├── mobile-services-concepts-links.md │ ├── mobile-services-disaster-recovery.md │ ├── mobile-services-dotnet-backend-android-get-started-push.md │ ├── mobile-services-dotnet-backend-android-get-started-users.md │ ├── mobile-services-dotnet-backend-android-get-started.md │ ├── mobile-services-dotnet-backend-define-custom-api.md │ ├── mobile-services-dotnet-backend-get-started-custom-authentication.md │ ├── mobile-services-dotnet-backend-how-to-configure-iis-express.md │ ├── mobile-services-dotnet-backend-how-to-troubleshoot.md │ ├── mobile-services-dotnet-backend-how-to-use-code-first-migrations.md │ ├── mobile-services-dotnet-backend-hybrid-connections-get-started.md │ ├── mobile-services-dotnet-backend-ios-adal-sso-authentication.md │ ├── mobile-services-dotnet-backend-ios-get-started-push.md │ ├── mobile-services-dotnet-backend-ios-get-started-users.md │ ├── mobile-services-dotnet-backend-ios-get-started.md │ ├── mobile-services-dotnet-backend-ios-push-notifications-app-users.md │ ├── mobile-services-dotnet-backend-schedule-recurring-tasks.md │ ├── mobile-services-dotnet-backend-service-side-authorization.md │ ├── mobile-services-dotnet-backend-store-code-source-control.md │ ├── mobile-services-dotnet-backend-store-data-table-storage.md │ ├── mobile-services-dotnet-backend-use-existing-sql-database.md │ ├── mobile-services-dotnet-backend-windows-store-dotnet-aad-rbac.md │ ├── mobile-services-dotnet-backend-windows-store-dotnet-get-started.md │ ├── mobile-services-dotnet-backend-windows-store-dotnet-leaderboard.md │ ├── mobile-services-dotnet-backend-windows-store-dotnet-push-notifications-app-users.md │ ├── mobile-services-dotnet-backend-windows-universal-dotnet-get-started-data.md │ ├── mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md │ ├── mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md │ ├── mobile-services-dotnet-backend-windows-universal-dotnet-upload-data-blob-storage.md │ ├── mobile-services-dotnet-backend-xamarin-android-get-started-push.md │ ├── mobile-services-dotnet-backend-xamarin-android-get-started-users.md │ ├── mobile-services-dotnet-backend-xamarin-android-get-started.md │ ├── mobile-services-dotnet-backend-xamarin-ios-get-started-users.md │ ├── mobile-services-dotnet-backend-xamarin-ios-get-started.md │ ├── mobile-services-dotnet-how-to-use-client-library.md │ ├── mobile-services-how-to-register-active-directory-authentication.md │ ├── mobile-services-how-to-register-facebook-authentication.md │ ├── mobile-services-how-to-register-google-authentication.md │ ├── mobile-services-how-to-register-microsoft-authentication.md │ ├── mobile-services-how-to-register-twitter-authentication.md │ ├── mobile-services-how-to-use-multiple-clients-single-service.md │ ├── mobile-services-how-to-use-server-scripts.md │ ├── mobile-services-html-get-started-users.md │ ├── mobile-services-html-get-started.md │ ├── mobile-services-html-how-to-use-client-library.md │ ├── mobile-services-ios-get-started-offline-data.md │ ├── mobile-services-ios-get-started-users.md │ ├── mobile-services-ios-get-started.md │ ├── mobile-services-ios-handling-conflicts-offline-data.md │ ├── mobile-services-ios-how-to-use-client-library.md │ ├── mobile-services-javascript-backend-android-get-started-push.md │ ├── mobile-services-javascript-backend-define-custom-api.md │ ├── mobile-services-javascript-backend-ios-get-started-push.md │ ├── mobile-services-javascript-backend-ios-push-notifications-app-users.md │ ├── mobile-services-javascript-backend-phonegap-get-started.md │ ├── mobile-services-javascript-backend-service-side-authorization.md │ ├── mobile-services-javascript-backend-windows-phone-get-started-push.md │ ├── mobile-services-javascript-backend-windows-store-dotnet-get-started.md │ ├── mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md │ ├── mobile-services-javascript-backend-windows-store-javascript-get-started.md │ ├── mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md │ ├── mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md │ ├── mobile-services-javascript-backend-windows-universal-dotnet-upload-data-blob-storage.md │ ├── mobile-services-manage-command-line-interface.md │ ├── mobile-services-schedule-recurring-tasks.md │ ├── mobile-services-sql-scale-guidance.md │ ├── mobile-services-store-scripts-source-control.md │ ├── mobile-services-using-soft-delete.md │ ├── mobile-services-windows-phone-get-started-data.md │ ├── mobile-services-windows-phone-get-started-users.md │ ├── mobile-services-windows-store-dotnet-adal-sso-authentication.md │ ├── mobile-services-windows-store-dotnet-get-started-offline-data.md │ ├── mobile-services-windows-store-dotnet-handle-database-conflicts.md │ ├── mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md │ ├── mobile-services-xamarin-android-get-started-offline-data.md │ ├── mobile-services-xamarin-ios-get-started-offline-data.md │ ├── partner-sencha-mobile-services-get-started.md │ ├── partner-twilio-mobile-services-how-to-use-voice-sms.md │ ├── partner-xamarin-mobile-services-android-get-started-push.md │ ├── partner-xamarin-mobile-services-android-get-started-users.md │ ├── partner-xamarin-mobile-services-android-get-started.md │ ├── partner-xamarin-mobile-services-ios-get-started-push.md │ ├── partner-xamarin-mobile-services-ios-get-started-users.md │ ├── partner-xamarin-mobile-services-ios-get-started.md │ ├── partner-xamarin-mobile-services-xamarin-forms-get-started-push.md │ ├── store-sendgrid-mobile-services-send-email-scripts.md │ ├── vs-mobile-services-cordova-getting-started.md │ ├── vs-mobile-services-cordova-what-happened.md │ ├── vs-mobile-services-dotnet-getting-started.md │ ├── vs-mobile-services-dotnet-what-happened.md │ ├── vs-mobile-services-javascript-getting-started.md │ └── vs-mobile-services-javascript-what-happened.md ├── sdk/ │ ├── Javascript/ │ │ ├── .gitignore │ │ ├── Gruntfile.js │ │ ├── Microsoft.WindowsAzure.Mobile.JS.sln │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Generated/ │ │ │ │ └── .gitignore │ │ │ ├── Internals/ │ │ │ │ ├── DevIntellisense.js │ │ │ │ ├── InternalsVisible.js │ │ │ │ └── NodeExports.js │ │ │ ├── LoginUis/ │ │ │ │ ├── BrowserPopup.js │ │ │ │ ├── CordovaPopup.js │ │ │ │ └── WebAuthBroker.js │ │ │ ├── Microsoft.WindowsAzure.Mobile.JS.csproj │ │ │ ├── MobileServiceClient.js │ │ │ ├── MobileServiceLogin.js │ │ │ ├── MobileServiceTable.js │ │ │ ├── MobileServices.intellisense.js │ │ │ ├── MobileServices.intellisense.txt │ │ │ ├── MobileServices.priconfig.xml │ │ │ ├── Platforms/ │ │ │ │ ├── Platform.Web.js │ │ │ │ └── Platform.WinJS.js │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Push/ │ │ │ │ ├── LocalStorageManager.js │ │ │ │ ├── Push.Web.js │ │ │ │ ├── Push.WinJS.js │ │ │ │ ├── PushHttpClient.js │ │ │ │ └── RegistrationManager.js │ │ │ ├── Require.js │ │ │ ├── Strings/ │ │ │ │ └── en-US/ │ │ │ │ └── Resources.resjson │ │ │ ├── Transports/ │ │ │ │ ├── DirectAjaxTransport.js │ │ │ │ └── IframeTransport.js │ │ │ └── Utilities/ │ │ │ ├── Extensions.js │ │ │ ├── PostMessageExchange.js │ │ │ ├── Promises.js │ │ │ └── Validate.js │ │ ├── test/ │ │ │ ├── framework/ │ │ │ │ ├── ActionContinuation.cs │ │ │ │ ├── Assert.cs │ │ │ │ ├── FunctionalTestFilter.cs │ │ │ │ ├── IAsyncExecution.cs │ │ │ │ ├── IContinuation.cs │ │ │ │ ├── ITestReporter.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.WinJS.TestFramework.csproj │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── RuntimeTestFilter.cs │ │ │ │ ├── Tagging/ │ │ │ │ │ ├── TagManager.ExpressionEvaluator.cs │ │ │ │ │ ├── TagManager.Tags.cs │ │ │ │ │ ├── TagManager.cs │ │ │ │ │ └── TagTestFilter.cs │ │ │ │ ├── TestFilter.cs │ │ │ │ ├── TestGroup.cs │ │ │ │ ├── TestHarness.cs │ │ │ │ ├── TestMethod.cs │ │ │ │ ├── TestSettings.cs │ │ │ │ └── WinJS/ │ │ │ │ ├── Formatter.cs │ │ │ │ ├── MessageEventArgs.cs │ │ │ │ ├── PromiseAsyncExecution.cs │ │ │ │ ├── PromiseAsyncExecutionEventArgs.cs │ │ │ │ └── TestReporter.cs │ │ │ ├── web/ │ │ │ │ ├── Microsoft.Azure.Zumo.Web.Test.csproj │ │ │ │ ├── Tests.library │ │ │ │ ├── Web.Debug.config │ │ │ │ ├── Web.Release.config │ │ │ │ ├── Web.config │ │ │ │ ├── css/ │ │ │ │ │ └── styles.css │ │ │ │ ├── generated/ │ │ │ │ │ └── .gitignore │ │ │ │ ├── index.html │ │ │ │ ├── js/ │ │ │ │ │ ├── TestClientHelper.js │ │ │ │ │ └── TestFrameworkAdapter.js │ │ │ │ ├── promiseTests/ │ │ │ │ │ ├── package.json │ │ │ │ │ ├── readme.txt │ │ │ │ │ └── test-promises.js │ │ │ │ └── tests/ │ │ │ │ └── unit/ │ │ │ │ └── push.js │ │ │ └── winJS/ │ │ │ ├── Microsoft.Azure.Zumo.Windows.WinJS.Test_TemporaryKey.pfx │ │ │ ├── Microsoft.WindowsAzure.Mobile.WinJS.Test.jsproj │ │ │ ├── Tests.library │ │ │ ├── css/ │ │ │ │ └── default.css │ │ │ ├── default.html │ │ │ ├── generated/ │ │ │ │ └── .gitignore │ │ │ ├── js/ │ │ │ │ └── default.js │ │ │ ├── package.appxmanifest │ │ │ ├── packages.config │ │ │ └── tests/ │ │ │ ├── TestFramework.js │ │ │ ├── TestInterface.js │ │ │ ├── functional/ │ │ │ │ ├── basics.js │ │ │ │ ├── blogging.js │ │ │ │ ├── dates.js │ │ │ │ ├── mobileServiceTable.Functional.js │ │ │ │ └── todo.js │ │ │ ├── unit/ │ │ │ │ ├── extensions.js │ │ │ │ ├── localStorageManager.js │ │ │ │ ├── mobileServiceTables.js │ │ │ │ ├── mobileServicesClient._request.js │ │ │ │ ├── mobileServicesClient.js │ │ │ │ ├── pushHttpClient.js │ │ │ │ ├── registrationManager.js │ │ │ │ └── validate.js │ │ │ ├── utilities/ │ │ │ │ ├── TableHelper.js │ │ │ │ └── constants.js │ │ │ └── winJsOnly/ │ │ │ └── push.js │ │ └── tools/ │ │ └── JSBuild/ │ │ ├── MergeJSModules.cs │ │ ├── Microsoft.WindowsAzure.Mobile.JSBuild.csproj │ │ └── Properties/ │ │ └── AssemblyInfo.cs │ ├── Managed/ │ │ ├── .gitignore │ │ ├── Microsoft.WindowsAzure.Mobile.Managed - IncludeXamarin.sln │ │ ├── Microsoft.WindowsAzure.Mobile.Managed.SQLiteStore.sln │ │ ├── Microsoft.WindowsAzure.Mobile.Managed.sln │ │ ├── src/ │ │ │ ├── Microsoft.WindowsAzure.MobileServices/ │ │ │ │ ├── Authentication/ │ │ │ │ │ ├── MobileServiceAuthentication.cs │ │ │ │ │ ├── MobileServiceAuthenticationProvider.cs │ │ │ │ │ └── MobileServiceTokenAuthentication.cs │ │ │ │ ├── Collections/ │ │ │ │ │ ├── MobileServiceCollection.cs │ │ │ │ │ └── MobileServiceCollectionEventArgs.cs │ │ │ │ ├── EnumValueAttribute.cs │ │ │ │ ├── Exceptions/ │ │ │ │ │ ├── MobileServiceConflictException.cs │ │ │ │ │ ├── MobileServiceInvalidOperationException.cs │ │ │ │ │ ├── MobileServiceLocalStoreException.cs │ │ │ │ │ ├── MobileServiceODataException.cs │ │ │ │ │ ├── MobileServicePreconditionFailedException.cs │ │ │ │ │ ├── MobileServicePushAbortException.cs │ │ │ │ │ └── MobileServicePushFailedException.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── ExceptionExtensions.cs │ │ │ │ │ ├── IDictionaryExtensions.cs │ │ │ │ │ ├── JTokenExtensions.cs │ │ │ │ │ ├── MobileServiceClientExtensions.cs │ │ │ │ │ ├── MobileServiceCollectionExtensions.cs │ │ │ │ │ ├── MobileServiceLocalStoreExtensions.cs │ │ │ │ │ ├── MobileServiceSyncContextExtensions.cs │ │ │ │ │ ├── MobileServiceSyncTableExtensions.cs │ │ │ │ │ ├── MobileServiceTableExtensions.cs │ │ │ │ │ ├── PlatformInformationExtensions.cs │ │ │ │ │ ├── StringExtensions.cs │ │ │ │ │ └── TypeExtensions.cs │ │ │ │ ├── Http/ │ │ │ │ │ ├── HttpUtility.cs │ │ │ │ │ ├── LinkHeaderValue.cs │ │ │ │ │ ├── MobileServiceHttpClient.cs │ │ │ │ │ └── MobileServiceHttpResponse.cs │ │ │ │ ├── IMobileServiceClient.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.csproj │ │ │ │ ├── MobileServiceClient.cs │ │ │ │ ├── MobileServiceFeatures.cs │ │ │ │ ├── MobileServiceUrlBuilder.cs │ │ │ │ ├── MobileServiceUser.cs │ │ │ │ ├── Platform/ │ │ │ │ │ ├── IApplicationStorage.cs │ │ │ │ │ ├── IExpressionUtility.cs │ │ │ │ │ ├── IPlatform.cs │ │ │ │ │ ├── IPlatformInformation.cs │ │ │ │ │ ├── IPushUtility.cs │ │ │ │ │ ├── Platform.cs │ │ │ │ │ └── PreserveAttribute.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Push/ │ │ │ │ │ ├── ILocalStorageManager.cs │ │ │ │ │ ├── IRegistrationManager.cs │ │ │ │ │ ├── LocalStorageManager.cs │ │ │ │ │ ├── PushHttpClient.cs │ │ │ │ │ ├── Registration.cs │ │ │ │ │ ├── RegistrationClassConverter.cs │ │ │ │ │ ├── RegistrationManager.cs │ │ │ │ │ └── StoredRegistrationEntry.cs │ │ │ │ ├── Resources.Designer.cs │ │ │ │ ├── Resources.resx │ │ │ │ ├── Table/ │ │ │ │ │ ├── IMobileServiceTable.Generic.cs │ │ │ │ │ ├── IMobileServiceTable.cs │ │ │ │ │ ├── MobileServiceObjectReader.cs │ │ │ │ │ ├── MobileServiceSystemColumns.cs │ │ │ │ │ ├── MobileServiceSystemProperties.cs │ │ │ │ │ ├── MobileServiceTable.Generic.cs │ │ │ │ │ ├── MobileServiceTable.cs │ │ │ │ │ ├── Query/ │ │ │ │ │ │ ├── IQueryResultEnumerable.cs │ │ │ │ │ │ ├── ITotalCountProvider.cs │ │ │ │ │ │ ├── Linq/ │ │ │ │ │ │ │ ├── FilterBuildingExpressionVisitor.cs │ │ │ │ │ │ │ ├── IMobileServiceTableQuery.cs │ │ │ │ │ │ │ ├── MemberInfoKey.cs │ │ │ │ │ │ │ ├── MobileServiceTableQuery.cs │ │ │ │ │ │ │ ├── MobileServiceTableQueryProvider.cs │ │ │ │ │ │ │ └── MobileServiceTableQueryTranslator.cs │ │ │ │ │ │ ├── MobileServiceTableQueryDescription.cs │ │ │ │ │ │ ├── OData/ │ │ │ │ │ │ │ ├── BinaryOperatorKind.cs │ │ │ │ │ │ │ ├── BinaryOperatorNode.cs │ │ │ │ │ │ │ ├── ConstantNode.cs │ │ │ │ │ │ │ ├── ConvertNode.cs │ │ │ │ │ │ │ ├── FunctionCallNode.cs │ │ │ │ │ │ │ ├── MemberAccessNode.cs │ │ │ │ │ │ │ ├── ODataExpressionLexer.cs │ │ │ │ │ │ │ ├── ODataExpressionParser.cs │ │ │ │ │ │ │ ├── ODataExpressionVisitor.cs │ │ │ │ │ │ │ ├── OrderByDirection.cs │ │ │ │ │ │ │ ├── OrderByNode.cs │ │ │ │ │ │ │ ├── QueryNode.cs │ │ │ │ │ │ │ ├── QueryNodeKind.cs │ │ │ │ │ │ │ ├── QueryNodeVisitor.cs │ │ │ │ │ │ │ ├── QueryToken.cs │ │ │ │ │ │ │ ├── QueryTokenKind.cs │ │ │ │ │ │ │ ├── UnaryOperatorKind.cs │ │ │ │ │ │ │ └── UnaryOperatorNode.cs │ │ │ │ │ │ ├── ODataOptions.cs │ │ │ │ │ │ ├── QueryResult.cs │ │ │ │ │ │ ├── QueryResultEnumerable.cs │ │ │ │ │ │ └── QueryResultList.cs │ │ │ │ │ ├── Serialization/ │ │ │ │ │ │ ├── CreatedAtAttribute.cs │ │ │ │ │ │ ├── DataTableAttribute.cs │ │ │ │ │ │ ├── DeletedAttribute.cs │ │ │ │ │ │ ├── ISystemPropertyAttribute.cs │ │ │ │ │ │ ├── MobileServiceContractResolver.cs │ │ │ │ │ │ ├── MobileServiceIsoDateTimeConverter.cs │ │ │ │ │ │ ├── MobileServiceJsonSerializerSettings.cs │ │ │ │ │ │ ├── MobileServicePrecisionCheckConverter.cs │ │ │ │ │ │ ├── MobileServiceSerializer.cs │ │ │ │ │ │ ├── UpdatedAtAttribute.cs │ │ │ │ │ │ └── VersionAttribute.cs │ │ │ │ │ └── Sync/ │ │ │ │ │ ├── IMobileServiceLocalStore.cs │ │ │ │ │ ├── IMobileServiceSyncContext.cs │ │ │ │ │ ├── IMobileServiceSyncHandler.cs │ │ │ │ │ ├── IMobileServiceSyncTable.Generic.cs │ │ │ │ │ ├── IMobileServiceSyncTable.cs │ │ │ │ │ ├── MobileServiceLocalStore.cs │ │ │ │ │ ├── MobileServiceLocalSystemTables.cs │ │ │ │ │ ├── MobileServicePushCompletionResult.cs │ │ │ │ │ ├── MobileServicePushStatus.cs │ │ │ │ │ ├── MobileServiceRemoteTableOptions.cs │ │ │ │ │ ├── MobileServiceSyncContext.cs │ │ │ │ │ ├── MobileServiceSyncHandler.cs │ │ │ │ │ ├── MobileServiceSyncSettingsManager.cs │ │ │ │ │ ├── MobileServiceSyncTable.Generic.cs │ │ │ │ │ ├── MobileServiceSyncTable.cs │ │ │ │ │ ├── PullOptions.cs │ │ │ │ │ └── Queue/ │ │ │ │ │ ├── Actions/ │ │ │ │ │ │ ├── IncrementalPullStrategy.cs │ │ │ │ │ │ ├── PullAction.cs │ │ │ │ │ │ ├── PullCursor.cs │ │ │ │ │ │ ├── PullStrategy.cs │ │ │ │ │ │ ├── PurgeAction.cs │ │ │ │ │ │ ├── PushAction.cs │ │ │ │ │ │ ├── SyncAction.cs │ │ │ │ │ │ └── TableAction.cs │ │ │ │ │ ├── OperationBatch.cs │ │ │ │ │ ├── OperationQueue.cs │ │ │ │ │ └── Operations/ │ │ │ │ │ ├── DeleteOperation.cs │ │ │ │ │ ├── IMobileServiceTableOperation.cs │ │ │ │ │ ├── InsertOperation.cs │ │ │ │ │ ├── MobileServiceTableKind.cs │ │ │ │ │ ├── MobileServiceTableOperation.cs │ │ │ │ │ ├── MobileServiceTableOperationError.cs │ │ │ │ │ ├── MobileServiceTableOperationKind.cs │ │ │ │ │ ├── MobileServiceTableOperationState.cs │ │ │ │ │ └── UpdateOperation.cs │ │ │ │ ├── Threading/ │ │ │ │ │ ├── ActionBlock.cs │ │ │ │ │ ├── AsyncLock.cs │ │ │ │ │ ├── AsyncLockDictionary.cs │ │ │ │ │ ├── AsyncReaderWriterLock.cs │ │ │ │ │ └── DisposeAction.cs │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.Android/ │ │ │ │ ├── Authentication/ │ │ │ │ │ └── MobileServiceUIAuthentication.cs │ │ │ │ ├── ExpressionUtility/ │ │ │ │ │ └── ExpressionVisitor.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ └── MobileServiceClientExtensions.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.Android.csproj │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ │ ├── CurrentPlatform.cs │ │ │ │ │ ├── PlatformInformation.cs │ │ │ │ │ └── PushUtility.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Push/ │ │ │ │ │ ├── GcmRegistration.cs │ │ │ │ │ ├── GcmTemplateRegistration.cs │ │ │ │ │ └── Push.cs │ │ │ │ ├── Resources/ │ │ │ │ │ └── Resource.designer.cs │ │ │ │ ├── app.config │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.Net45/ │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.Net45.csproj │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ │ ├── CurrentPlatform.cs │ │ │ │ │ └── PlatformInformation.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── app.config │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore/ │ │ │ │ ├── ColumnDefinition.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ └── EnumerableExtensions.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.csproj │ │ │ │ ├── MobileServiceSQLiteStore.cs │ │ │ │ ├── MobileServiceSQLiteStoreExtensions.cs │ │ │ │ ├── Properties/ │ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ │ ├── Resources.Designer.cs │ │ │ │ │ └── Resources.resx │ │ │ │ ├── SqlColumnType.cs │ │ │ │ ├── SqlHelpers.cs │ │ │ │ ├── SqlQueryFormatter.cs │ │ │ │ ├── TableDefinition.cs │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsPhone8/ │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.WP8.csproj │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ │ ├── PlatformInformation.cs │ │ │ │ │ └── PushUtility.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Push/ │ │ │ │ │ ├── MpnsRegistration.cs │ │ │ │ │ ├── MpnsTemplateRegistration.cs │ │ │ │ │ └── Push.cs │ │ │ │ ├── app.config │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsPhone8.UI/ │ │ │ │ ├── Authentication/ │ │ │ │ │ └── AuthenticationBroker.cs │ │ │ │ ├── LoginPage.xaml │ │ │ │ ├── LoginPage.xaml.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.UI.WP8.csproj │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── app.config │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsPhone81/ │ │ │ │ ├── Authentication/ │ │ │ │ │ └── AuthenticationBroker.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ └── MobileServiceClientExtensions.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.WP81.csproj │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ │ └── PlatformInformation.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── app.config │ │ │ │ └── packages.config │ │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsStore/ │ │ │ │ ├── App.config │ │ │ │ ├── Authentication/ │ │ │ │ │ ├── AuthenticationBroker.cs │ │ │ │ │ ├── MobileServiceSingleSignOnAuthentication.cs │ │ │ │ │ └── MobileServiceUIAuthentication.cs │ │ │ │ ├── Collections/ │ │ │ │ │ └── MobileServiceIncrementalLoadingCollection.cs │ │ │ │ ├── ExpressionUtility/ │ │ │ │ │ ├── PartialEvaluator.cs │ │ │ │ │ └── VisitorHelper.cs │ │ │ │ ├── Extensions/ │ │ │ │ │ ├── MobileServiceClientExtensions.cs │ │ │ │ │ ├── MobileServiceCollectionViewExtensions.cs │ │ │ │ │ ├── MobileServiceIncrementalLoadingCollectionExtensions.cs │ │ │ │ │ └── SingleSignOnExtensions.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.Win8.csproj │ │ │ │ ├── MobileServices.priconfig.xml │ │ │ │ ├── Platform/ │ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ │ ├── CurrentPlatform.cs │ │ │ │ │ ├── ExpressionUtility.cs │ │ │ │ │ ├── PlatformInformation.cs │ │ │ │ │ └── PushUtility.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── AssemblyInfo.cs │ │ │ │ ├── Push/ │ │ │ │ │ ├── Push.cs │ │ │ │ │ ├── WnsRegistration.cs │ │ │ │ │ └── WnsTemplateRegistration.cs │ │ │ │ └── packages.config │ │ │ └── Microsoft.WindowsAzure.MobileServices.iOS/ │ │ │ ├── Authentication/ │ │ │ │ └── MobileServiceUIAuthentication.cs │ │ │ ├── Extensions/ │ │ │ │ └── MobileServiceClientExtensions.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.iOS-Classic.csproj │ │ │ ├── Microsoft.WindowsAzure.Mobile.Ext.iOS.csproj │ │ │ ├── Platform/ │ │ │ │ ├── ApplicationStorage.cs │ │ │ │ ├── CurrentPlatform.cs │ │ │ │ ├── PlatformInformation.cs │ │ │ │ └── PushUtility.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Push/ │ │ │ │ ├── ApnsRegistration.cs │ │ │ │ ├── ApnsTemplateRegistration.cs │ │ │ │ └── Push.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ └── test/ │ │ ├── Microsoft.WindowsAzure.Mobile.Net45.Test/ │ │ │ ├── App.config │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── Common/ │ │ │ │ └── StandardStyles.xaml │ │ │ ├── ConsoleHelper.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Net45.Test.csproj │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── Resources.Designer.cs │ │ │ │ └── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ ├── Settings.settings │ │ │ ├── UI/ │ │ │ │ ├── MainPage.xaml │ │ │ │ ├── MainPage.xaml.cs │ │ │ │ ├── MainWindow.xaml │ │ │ │ ├── MainWindow.xaml.cs │ │ │ │ ├── TestPage.xaml │ │ │ │ └── TestPage.xaml.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.Mobile.Net45.Vb.Test/ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Net45.Vb.Test.vbproj │ │ │ ├── My Project/ │ │ │ │ ├── Application.Designer.vb │ │ │ │ ├── Application.myapp │ │ │ │ └── AssemblyInfo.vb │ │ │ ├── QueryTests.vb │ │ │ ├── TestsForOptionCompareBinary.vb │ │ │ ├── TestsForOptionCompareText.vb │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.Android.Test/ │ │ │ ├── App.cs │ │ │ ├── GroupDescription.cs │ │ │ ├── HarnessActivity.cs │ │ │ ├── LoginActivity.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Android.Test.csproj │ │ │ ├── Properties/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ ├── Layout/ │ │ │ │ │ ├── Harness.axml │ │ │ │ │ ├── ListedGroup.axml │ │ │ │ │ ├── ListedTest.axml │ │ │ │ │ ├── Login.axml │ │ │ │ │ └── Test.axml │ │ │ │ ├── Resource.Designer.cs │ │ │ │ └── Values/ │ │ │ │ └── Strings.xml │ │ │ ├── TestActivity.cs │ │ │ ├── TestDescription.cs │ │ │ ├── TestListener.cs │ │ │ ├── TestPlatform/ │ │ │ │ └── PushTestUtility.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.Android.Test/ │ │ │ ├── App.cs │ │ │ ├── GroupDescription.cs │ │ │ ├── HarnessActivity.cs │ │ │ ├── LoginActivity.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Android.Test.csproj │ │ │ ├── Properties/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ ├── Layout/ │ │ │ │ │ ├── Harness.axml │ │ │ │ │ ├── ListedGroup.axml │ │ │ │ │ ├── ListedTest.axml │ │ │ │ │ ├── Login.axml │ │ │ │ │ └── Test.axml │ │ │ │ ├── Resource.Designer.cs │ │ │ │ └── Values/ │ │ │ │ └── Strings.xml │ │ │ ├── TestActivity.cs │ │ │ ├── TestDescription.cs │ │ │ ├── TestListener.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.Net45.Test/ │ │ │ ├── App.config │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── Common/ │ │ │ │ └── StandardStyles.xaml │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Net45.Test.csproj │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ ├── Resources.Designer.cs │ │ │ │ └── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ ├── Settings.settings │ │ │ ├── UI/ │ │ │ │ ├── MainPage.xaml │ │ │ │ ├── MainPage.xaml.cs │ │ │ │ ├── MainWindow.xaml │ │ │ │ ├── MainWindow.xaml.cs │ │ │ │ ├── TestPage.xaml │ │ │ │ └── TestPage.xaml.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test/ │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Test.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── TestUtilities.cs │ │ │ ├── UnitTests/ │ │ │ │ ├── SQLiteStoreTests.Integration.cs │ │ │ │ ├── SQLiteStoreTests.Query.cs │ │ │ │ ├── SQLiteStoreTests.cs │ │ │ │ └── SyncSettingsManagerTests.Integration.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.Unit/ │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Test.Unit.csproj │ │ │ ├── MobileServiceSQLiteStoreExtensionTests.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── SqlHelperTests.cs │ │ │ ├── SqlQueryFormatterTests.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.WindowsPhone8.Test/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── LocalizedStrings.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.WP8.Test.csproj │ │ │ ├── Properties/ │ │ │ │ ├── AppManifest.xml │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── WMAppManifest.xml │ │ │ ├── Resources/ │ │ │ │ ├── AppResources.Designer.cs │ │ │ │ └── AppResources.resx │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.WindowsStore.Test/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── Common/ │ │ │ │ └── StandardStyles.xaml │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Win8.Test.csproj │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.Win8.Test_TemporaryKey.pfx │ │ │ ├── Mocks/ │ │ │ │ └── MobileServiceTableQueryMock.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── UI/ │ │ │ │ ├── GroupDescription.cs │ │ │ │ ├── MainPage.xaml │ │ │ │ ├── MainPage.xaml.cs │ │ │ │ ├── Microsoft.WindowsAzure.Mobile.Win8.Test_TemporaryKey.pfx │ │ │ │ ├── TestDescription.cs │ │ │ │ ├── TestPage.xaml │ │ │ │ └── TestPage.xaml.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.SQLiteStore.iOS.Test/ │ │ │ ├── AppDelegate.cs │ │ │ ├── Info.plist │ │ │ ├── Main.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.SQLiteStore.iOS.Test.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── UI/ │ │ │ │ ├── HarnessViewController.cs │ │ │ │ ├── LoginViewController.cs │ │ │ │ └── TestViewController.cs │ │ │ ├── app.config │ │ │ ├── packages.config │ │ │ └── readme.txt │ │ ├── Microsoft.WindowsAzure.MobileServices.Test/ │ │ │ ├── AssertEx.cs │ │ │ ├── FunctionalTests/ │ │ │ │ ├── BloggingTest.cs │ │ │ │ ├── Book.cs │ │ │ │ ├── DataSourceTest.cs │ │ │ │ ├── DateTests.cs │ │ │ │ ├── FunctionalTestBase.cs │ │ │ │ ├── QueryingTest.cs │ │ │ │ ├── Table/ │ │ │ │ │ └── MobileServiceTableTest.cs │ │ │ │ └── ToDoTest.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Test.csproj │ │ │ ├── Mocks/ │ │ │ │ ├── MobileServiceLocalStoreMock.cs │ │ │ │ ├── MobileServiceSyncHandlerMock.cs │ │ │ │ └── MobileServiceTableQueryMock.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── SerializationTypes/ │ │ │ │ ├── BaseTypes.cs │ │ │ │ ├── ComplexType.cs │ │ │ │ ├── ConverterType.cs │ │ │ │ ├── DataContractDerivedDataContractType.cs │ │ │ │ ├── DataContractDerivedJsonPropertyType.cs │ │ │ │ ├── DataContractDerivedPocoType.cs │ │ │ │ ├── DataContractType.cs │ │ │ │ ├── DataMemberType.cs │ │ │ │ ├── DataTableType.cs │ │ │ │ ├── DerivedDuplicateKeyType.cs │ │ │ │ ├── DuplicateKeyType.cs │ │ │ │ ├── IdTypes.cs │ │ │ │ ├── InterfacePropertyType.cs │ │ │ │ ├── JObjectTypes.cs │ │ │ │ ├── JsonContainerType.cs │ │ │ │ ├── JsonPropertyDerivedDataContractType.cs │ │ │ │ ├── JsonPropertyDerivedJsonPropertyType.cs │ │ │ │ ├── JsonPropertyType.cs │ │ │ │ ├── PocoDerivedDataContractType.cs │ │ │ │ ├── PocoDerivedJsonPropertyType.cs │ │ │ │ ├── PocoDerivedPocoType.cs │ │ │ │ ├── PocoType.cs │ │ │ │ ├── SerializationTypeUtility.cs │ │ │ │ ├── SimpleTreeType.cs │ │ │ │ ├── SystemPropertyTypes.cs │ │ │ │ ├── TestConverter.cs │ │ │ │ └── TypeWithConstructor.cs │ │ │ ├── TestData/ │ │ │ │ ├── IdTestData.cs │ │ │ │ └── SystemPropertiesTestData.cs │ │ │ ├── TestPlatform/ │ │ │ │ ├── IPushTestUtility.cs │ │ │ │ ├── ITestPlatform.cs │ │ │ │ ├── MissingPushTestUtility.cs │ │ │ │ ├── MissingTestPlatform.cs │ │ │ │ └── TestPlatform.cs │ │ │ ├── UnitTests/ │ │ │ │ ├── Authentication/ │ │ │ │ │ └── MobileServiceTokenAuthenticationTests.cs │ │ │ │ ├── Collections/ │ │ │ │ │ └── MobileServiceCollection.Test.cs │ │ │ │ ├── Http/ │ │ │ │ │ └── HttpUtilityTests.cs │ │ │ │ ├── HttpHandlers/ │ │ │ │ │ ├── ComplexDelegatingHandler.cs │ │ │ │ │ └── TestHttpHandler.cs │ │ │ │ ├── MobileServiceClient.Test.cs │ │ │ │ ├── MobileServiceFeatures.Test.cs │ │ │ │ ├── MobileServiceUrlBuilder.Test.cs │ │ │ │ ├── MobileServiceUser.Test.cs │ │ │ │ ├── OData/ │ │ │ │ │ └── ODataExpressionParser.Test.cs │ │ │ │ ├── Push/ │ │ │ │ │ ├── LocalStorageMananger.Test.cs │ │ │ │ │ └── PushHttpClient.Test.cs │ │ │ │ └── Table/ │ │ │ │ ├── MobileServiceObjectReaderTests.cs │ │ │ │ ├── MobileServiceTable.Generic.Test.cs │ │ │ │ ├── MobileServiceTable.Test.cs │ │ │ │ ├── Query/ │ │ │ │ │ ├── MemberInfoKey.Test.cs │ │ │ │ │ └── ZumoQuery.Test.cs │ │ │ │ ├── Serialization/ │ │ │ │ │ ├── MobileServiceContractResolver.Test.cs │ │ │ │ │ ├── MobileServiceIsoDateTimeConverter.Test.cs │ │ │ │ │ ├── MobileServicePrecisionCheckConverter.Test.cs │ │ │ │ │ └── MobileServiceSerializer.Test.cs │ │ │ │ └── Sync/ │ │ │ │ ├── MobileServiceSyncContext.Test.cs │ │ │ │ ├── MobileServiceSyncTable.Generic.Test.cs │ │ │ │ └── MobileServiceSyncTable.Test.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.Test.Unit/ │ │ │ ├── Extensions/ │ │ │ │ ├── ExceptionExtensions.Test.cs │ │ │ │ └── JTokenExtensionsTests.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Test.Unit.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Table/ │ │ │ │ ├── MobileServiceSyncContextTests.cs │ │ │ │ ├── Query/ │ │ │ │ │ └── MobileServiceTableQueryDescriptionTests.cs │ │ │ │ └── Sync/ │ │ │ │ ├── MobileServiceSyncTableTests.cs │ │ │ │ └── Queue/ │ │ │ │ ├── Actions/ │ │ │ │ │ ├── PullActionTests.cs │ │ │ │ │ └── PushActionTests.cs │ │ │ │ └── Operations/ │ │ │ │ ├── DeleteOperationTests.cs │ │ │ │ ├── InsertOperationTests.cs │ │ │ │ ├── MobileServiceTableOperationErrorTests.cs │ │ │ │ ├── MobileServiceTableOperationTests.cs │ │ │ │ └── UpdateOperationTests.cs │ │ │ ├── Threading/ │ │ │ │ └── AsyncLockDictionaryTests.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.TestFramework/ │ │ │ ├── Framework/ │ │ │ │ ├── ActionContinuation.cs │ │ │ │ ├── Assert.cs │ │ │ │ ├── AsyncTestMethodAsyncAction.cs │ │ │ │ ├── AsyncTestMethodAttribute.cs │ │ │ │ ├── ExcludeTestAttribute.cs │ │ │ │ ├── FunctionalTestAttribute.cs │ │ │ │ ├── FunctionalTestFilter.cs │ │ │ │ ├── IAsyncExecution.cs │ │ │ │ ├── IContinuation.cs │ │ │ │ ├── ITestReporter.cs │ │ │ │ ├── TagAttribute.cs │ │ │ │ ├── TagManager.ExpressionEvaluator.cs │ │ │ │ ├── TagManager.Tags.cs │ │ │ │ ├── TagManager.cs │ │ │ │ ├── TagTestFilter.cs │ │ │ │ ├── TestBase.cs │ │ │ │ ├── TestFilter.cs │ │ │ │ ├── TestGroup.cs │ │ │ │ ├── TestHarness.cs │ │ │ │ ├── TestMethod.cs │ │ │ │ ├── TestMethodAsyncAction.cs │ │ │ │ ├── TestMethodAttribute.cs │ │ │ │ └── TestSettings.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.TestFramework.csproj │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsPhone8.Test/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── LocalizedStrings.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.WP8.Test.csproj │ │ │ ├── Package.appxmanifest │ │ │ ├── PlatformTests/ │ │ │ │ └── LoginTests.cs │ │ │ ├── Properties/ │ │ │ │ ├── AppManifest.xml │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── WMAppManifest.xml │ │ │ ├── Resources/ │ │ │ │ ├── AppResources.Designer.cs │ │ │ │ └── AppResources.resx │ │ │ ├── TestPlatform/ │ │ │ │ └── PushTestUtility.cs │ │ │ ├── UnitTests/ │ │ │ │ └── MobileServiceSerializer.Test.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsPhone81.Test/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.WP81.Test.csproj │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── UI/ │ │ │ │ ├── LoginPage.xaml │ │ │ │ └── LoginPage.xaml.cs │ │ │ ├── app.config │ │ │ └── packages.config │ │ ├── Microsoft.WindowsAzure.MobileServices.WindowsStore.Test/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── Common/ │ │ │ │ └── StandardStyles.xaml │ │ │ ├── Functional/ │ │ │ │ └── PushFunctional.Test.cs │ │ │ ├── Microsoft.WindowsAzure.Mobile.Win8.Test.csproj │ │ │ ├── Microsoft.WindowsAzure.Mobile.Win8.Test_TemporaryKey.pfx │ │ │ ├── Mocks/ │ │ │ │ └── MobileServiceTableQueryMock.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── PlatformTests/ │ │ │ │ └── LoginTests.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── TestPlatform/ │ │ │ │ ├── CurrentTestPlatform.cs │ │ │ │ └── PushTestUtility.cs │ │ │ ├── UI/ │ │ │ │ ├── GroupDescription.cs │ │ │ │ ├── LoginPage.xaml │ │ │ │ ├── LoginPage.xaml.cs │ │ │ │ ├── MainPage.xaml │ │ │ │ ├── MainPage.xaml.cs │ │ │ │ ├── TestDescription.cs │ │ │ │ ├── TestPage.xaml │ │ │ │ └── TestPage.xaml.cs │ │ │ ├── UnitTests/ │ │ │ │ ├── Collections/ │ │ │ │ │ └── IncrementalLoadingMobileServiceCollection.Test.cs │ │ │ │ └── PushUnit.Test.cs │ │ │ └── packages.config │ │ └── Microsoft.WindowsAzure.MobileServices.iOS.Test/ │ │ ├── AppDelegate.cs │ │ ├── Info.plist │ │ ├── Main.cs │ │ ├── Microsoft.WindowsAzure.Mobile.iOS.Test.csproj │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── TestPlatform/ │ │ │ └── PushTestUtility.cs │ │ ├── UI/ │ │ │ ├── HarnessViewController.cs │ │ │ ├── LoginViewController.cs │ │ │ └── TestViewController.cs │ │ ├── UnitTests/ │ │ │ └── PushPlatformTest.cs │ │ ├── app.config │ │ ├── packages.config │ │ └── readme.txt │ ├── android/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── android-libraries.gradle │ │ ├── build.gradle │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ └── gradle-wrapper.properties │ │ ├── gradle.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── settings.gradle │ │ ├── src/ │ │ │ ├── notifications-handler/ │ │ │ │ ├── .settings/ │ │ │ │ │ └── org.eclipse.jdt.core.prefs │ │ │ │ ├── build.gradle │ │ │ │ ├── doc/ │ │ │ │ │ ├── allclasses-frame.html │ │ │ │ │ ├── allclasses-noframe.html │ │ │ │ │ ├── com/ │ │ │ │ │ │ └── microsoft/ │ │ │ │ │ │ └── windowsazure/ │ │ │ │ │ │ └── notifications/ │ │ │ │ │ │ ├── BuildConfig.html │ │ │ │ │ │ ├── NotificationsBroadcastReceiver.html │ │ │ │ │ │ ├── NotificationsHandler.html │ │ │ │ │ │ ├── NotificationsManager.html │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ ├── BuildConfig.html │ │ │ │ │ │ │ ├── NotificationsBroadcastReceiver.html │ │ │ │ │ │ │ ├── NotificationsHandler.html │ │ │ │ │ │ │ └── NotificationsManager.html │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ └── package-use.html │ │ │ │ │ ├── constant-values.html │ │ │ │ │ ├── help-doc.html │ │ │ │ │ ├── index-files/ │ │ │ │ │ │ ├── index-1.html │ │ │ │ │ │ ├── index-2.html │ │ │ │ │ │ ├── index-3.html │ │ │ │ │ │ ├── index-4.html │ │ │ │ │ │ ├── index-5.html │ │ │ │ │ │ ├── index-6.html │ │ │ │ │ │ └── index-7.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── overview-tree.html │ │ │ │ │ ├── package-list │ │ │ │ │ └── stylesheet.css │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── microsoft/ │ │ │ │ │ └── windowsazure/ │ │ │ │ │ └── notifications/ │ │ │ │ │ ├── NotificationsBroadcastReceiver.java │ │ │ │ │ ├── NotificationsHandler.java │ │ │ │ │ └── NotificationsManager.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ └── strings.xml │ │ │ ├── sdk/ │ │ │ │ ├── .pmd │ │ │ │ ├── .settings/ │ │ │ │ │ └── org.eclipse.jdt.core.prefs │ │ │ │ ├── build.gradle │ │ │ │ ├── doc/ │ │ │ │ │ ├── allclasses-frame.html │ │ │ │ │ ├── allclasses-noframe.html │ │ │ │ │ ├── com/ │ │ │ │ │ │ └── microsoft/ │ │ │ │ │ │ └── windowsazure/ │ │ │ │ │ │ └── mobileservices/ │ │ │ │ │ │ ├── ApiJsonOperationCallback.html │ │ │ │ │ │ ├── ApiOperationCallback.html │ │ │ │ │ │ ├── BuildConfig.html │ │ │ │ │ │ ├── MobileServiceApplication.html │ │ │ │ │ │ ├── MobileServiceClient.html │ │ │ │ │ │ ├── MobileServiceException.html │ │ │ │ │ │ ├── MobileServiceFeatures.html │ │ │ │ │ │ ├── MobileServiceList.html │ │ │ │ │ │ ├── ServiceFilterResponseCallback.html │ │ │ │ │ │ ├── UserAuthenticationCallback.html │ │ │ │ │ │ ├── authentication/ │ │ │ │ │ │ │ ├── LoginManager.html │ │ │ │ │ │ │ ├── MobileServiceAuthenticationProvider.html │ │ │ │ │ │ │ ├── MobileServiceUser.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── LoginManager.html │ │ │ │ │ │ │ │ ├── MobileServiceAuthenticationProvider.html │ │ │ │ │ │ │ │ └── MobileServiceUser.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ ├── ApiJsonOperationCallback.html │ │ │ │ │ │ │ ├── ApiOperationCallback.html │ │ │ │ │ │ │ ├── BuildConfig.html │ │ │ │ │ │ │ ├── MobileServiceApplication.html │ │ │ │ │ │ │ ├── MobileServiceClient.html │ │ │ │ │ │ │ ├── MobileServiceException.html │ │ │ │ │ │ │ ├── MobileServiceFeatures.html │ │ │ │ │ │ │ ├── MobileServiceList.html │ │ │ │ │ │ │ ├── ServiceFilterResponseCallback.html │ │ │ │ │ │ │ └── UserAuthenticationCallback.html │ │ │ │ │ │ ├── http/ │ │ │ │ │ │ │ ├── AndroidHttpClientFactory.html │ │ │ │ │ │ │ ├── AndroidHttpClientFactoryImpl.html │ │ │ │ │ │ │ ├── HttpPatch.html │ │ │ │ │ │ │ ├── MobileServiceConnection.html │ │ │ │ │ │ │ ├── MobileServiceHttpClient.html │ │ │ │ │ │ │ ├── NextServiceFilterCallback.html │ │ │ │ │ │ │ ├── RequestAsyncTask.html │ │ │ │ │ │ │ ├── ServiceFilter.html │ │ │ │ │ │ │ ├── ServiceFilterRequest.html │ │ │ │ │ │ │ ├── ServiceFilterRequestImpl.html │ │ │ │ │ │ │ ├── ServiceFilterResponse.html │ │ │ │ │ │ │ ├── ServiceFilterResponseImpl.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── AndroidHttpClientFactory.html │ │ │ │ │ │ │ │ ├── AndroidHttpClientFactoryImpl.html │ │ │ │ │ │ │ │ ├── HttpPatch.html │ │ │ │ │ │ │ │ ├── MobileServiceConnection.html │ │ │ │ │ │ │ │ ├── MobileServiceHttpClient.html │ │ │ │ │ │ │ │ ├── NextServiceFilterCallback.html │ │ │ │ │ │ │ │ ├── RequestAsyncTask.html │ │ │ │ │ │ │ │ ├── ServiceFilter.html │ │ │ │ │ │ │ │ ├── ServiceFilterRequest.html │ │ │ │ │ │ │ │ ├── ServiceFilterRequestImpl.html │ │ │ │ │ │ │ │ ├── ServiceFilterResponse.html │ │ │ │ │ │ │ │ └── ServiceFilterResponseImpl.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ ├── notifications/ │ │ │ │ │ │ │ ├── GcmNativeRegistration.html │ │ │ │ │ │ │ ├── GcmTemplateRegistration.html │ │ │ │ │ │ │ ├── MobileServicePush.html │ │ │ │ │ │ │ ├── Registration.html │ │ │ │ │ │ │ ├── RegistrationCallback.html │ │ │ │ │ │ │ ├── RegistrationGoneException.html │ │ │ │ │ │ │ ├── TemplateRegistration.html │ │ │ │ │ │ │ ├── TemplateRegistrationCallback.html │ │ │ │ │ │ │ ├── UnregisterCallback.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── GcmNativeRegistration.html │ │ │ │ │ │ │ │ ├── GcmTemplateRegistration.html │ │ │ │ │ │ │ │ ├── MobileServicePush.html │ │ │ │ │ │ │ │ ├── Registration.html │ │ │ │ │ │ │ │ ├── RegistrationCallback.html │ │ │ │ │ │ │ │ ├── RegistrationGoneException.html │ │ │ │ │ │ │ │ ├── TemplateRegistration.html │ │ │ │ │ │ │ │ ├── TemplateRegistrationCallback.html │ │ │ │ │ │ │ │ └── UnregisterCallback.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ ├── package-use.html │ │ │ │ │ │ ├── table/ │ │ │ │ │ │ │ ├── MobileServiceJsonTable.html │ │ │ │ │ │ │ ├── MobileServicePreconditionFailedException.html │ │ │ │ │ │ │ ├── MobileServicePreconditionFailedExceptionBase.html │ │ │ │ │ │ │ ├── MobileServiceSystemProperty.html │ │ │ │ │ │ │ ├── MobileServiceTable.html │ │ │ │ │ │ │ ├── MobileServiceTableSystemPropertiesProvider.html │ │ │ │ │ │ │ ├── TableDeleteCallback.html │ │ │ │ │ │ │ ├── TableJsonOperationCallback.html │ │ │ │ │ │ │ ├── TableJsonQueryCallback.html │ │ │ │ │ │ │ ├── TableOperationCallback.html │ │ │ │ │ │ │ ├── TableQueryCallback.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── MobileServiceJsonTable.html │ │ │ │ │ │ │ │ ├── MobileServicePreconditionFailedException.html │ │ │ │ │ │ │ │ ├── MobileServicePreconditionFailedExceptionBase.html │ │ │ │ │ │ │ │ ├── MobileServiceSystemProperty.html │ │ │ │ │ │ │ │ ├── MobileServiceTable.html │ │ │ │ │ │ │ │ ├── MobileServiceTableSystemPropertiesProvider.html │ │ │ │ │ │ │ │ ├── TableDeleteCallback.html │ │ │ │ │ │ │ │ ├── TableJsonOperationCallback.html │ │ │ │ │ │ │ │ ├── TableJsonQueryCallback.html │ │ │ │ │ │ │ │ ├── TableOperationCallback.html │ │ │ │ │ │ │ │ └── TableQueryCallback.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ ├── package-use.html │ │ │ │ │ │ │ ├── query/ │ │ │ │ │ │ │ │ ├── ExecutableJsonQuery.html │ │ │ │ │ │ │ │ ├── ExecutableQuery.html │ │ │ │ │ │ │ │ ├── Query.html │ │ │ │ │ │ │ │ ├── QueryException.html │ │ │ │ │ │ │ │ ├── QueryNodeSQLWriter.html │ │ │ │ │ │ │ │ ├── QueryODataWriter.html │ │ │ │ │ │ │ │ ├── QueryOperations.html │ │ │ │ │ │ │ │ ├── QueryOrder.html │ │ │ │ │ │ │ │ ├── QuerySQLWriter.html │ │ │ │ │ │ │ │ ├── UnaryOperatorKind.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── ExecutableJsonQuery.html │ │ │ │ │ │ │ │ │ ├── ExecutableQuery.html │ │ │ │ │ │ │ │ │ ├── Query.html │ │ │ │ │ │ │ │ │ ├── QueryException.html │ │ │ │ │ │ │ │ │ ├── QueryNodeSQLWriter.html │ │ │ │ │ │ │ │ │ ├── QueryODataWriter.html │ │ │ │ │ │ │ │ │ ├── QueryOperations.html │ │ │ │ │ │ │ │ │ ├── QueryOrder.html │ │ │ │ │ │ │ │ │ ├── QuerySQLWriter.html │ │ │ │ │ │ │ │ │ └── UnaryOperatorKind.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ ├── serialization/ │ │ │ │ │ │ │ │ ├── DateSerializer.html │ │ │ │ │ │ │ │ ├── JsonEntityParser.html │ │ │ │ │ │ │ │ ├── LongSerializer.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── DateSerializer.html │ │ │ │ │ │ │ │ │ ├── JsonEntityParser.html │ │ │ │ │ │ │ │ │ └── LongSerializer.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ └── sync/ │ │ │ │ │ │ │ ├── MobileServiceJsonSyncTable.html │ │ │ │ │ │ │ ├── MobileServiceSyncContext.html │ │ │ │ │ │ │ ├── MobileServiceSyncTable.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── MobileServiceJsonSyncTable.html │ │ │ │ │ │ │ │ ├── MobileServiceSyncContext.html │ │ │ │ │ │ │ │ └── MobileServiceSyncTable.html │ │ │ │ │ │ │ ├── localstore/ │ │ │ │ │ │ │ │ ├── ColumnDataType.html │ │ │ │ │ │ │ │ ├── MobileServiceLocalStore.html │ │ │ │ │ │ │ │ ├── MobileServiceLocalStoreException.html │ │ │ │ │ │ │ │ ├── SQLiteLocalStore.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── ColumnDataType.html │ │ │ │ │ │ │ │ │ ├── MobileServiceLocalStore.html │ │ │ │ │ │ │ │ │ ├── MobileServiceLocalStoreException.html │ │ │ │ │ │ │ │ │ └── SQLiteLocalStore.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ ├── operations/ │ │ │ │ │ │ │ │ ├── DeleteOperation.html │ │ │ │ │ │ │ │ ├── InsertOperation.html │ │ │ │ │ │ │ │ ├── LocalTableOperationProcessor.html │ │ │ │ │ │ │ │ ├── RemoteTableOperationProcessor.html │ │ │ │ │ │ │ │ ├── TableOperation.html │ │ │ │ │ │ │ │ ├── TableOperationCollapser.html │ │ │ │ │ │ │ │ ├── TableOperationError.html │ │ │ │ │ │ │ │ ├── TableOperationKind.html │ │ │ │ │ │ │ │ ├── TableOperationVisitor.html │ │ │ │ │ │ │ │ ├── UpdateOperation.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── DeleteOperation.html │ │ │ │ │ │ │ │ │ ├── InsertOperation.html │ │ │ │ │ │ │ │ │ ├── LocalTableOperationProcessor.html │ │ │ │ │ │ │ │ │ ├── RemoteTableOperationProcessor.html │ │ │ │ │ │ │ │ │ ├── TableOperation.html │ │ │ │ │ │ │ │ │ ├── TableOperationCollapser.html │ │ │ │ │ │ │ │ │ ├── TableOperationError.html │ │ │ │ │ │ │ │ │ ├── TableOperationKind.html │ │ │ │ │ │ │ │ │ ├── TableOperationVisitor.html │ │ │ │ │ │ │ │ │ └── UpdateOperation.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ ├── package-use.html │ │ │ │ │ │ │ ├── push/ │ │ │ │ │ │ │ │ ├── MobileServicePushCompletionResult.html │ │ │ │ │ │ │ │ ├── MobileServicePushFailedException.html │ │ │ │ │ │ │ │ ├── MobileServicePushStatus.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── MobileServicePushCompletionResult.html │ │ │ │ │ │ │ │ │ ├── MobileServicePushFailedException.html │ │ │ │ │ │ │ │ │ └── MobileServicePushStatus.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ ├── queue/ │ │ │ │ │ │ │ │ ├── OperationErrorList.html │ │ │ │ │ │ │ │ ├── OperationQueue.Bookmark.html │ │ │ │ │ │ │ │ ├── OperationQueue.html │ │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ │ ├── OperationErrorList.html │ │ │ │ │ │ │ │ │ ├── OperationQueue.Bookmark.html │ │ │ │ │ │ │ │ │ └── OperationQueue.html │ │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ │ └── synchandler/ │ │ │ │ │ │ │ ├── MobileServiceSyncHandler.html │ │ │ │ │ │ │ ├── MobileServiceSyncHandlerException.html │ │ │ │ │ │ │ ├── SimpleSyncHandler.html │ │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ │ ├── MobileServiceSyncHandler.html │ │ │ │ │ │ │ │ ├── MobileServiceSyncHandlerException.html │ │ │ │ │ │ │ │ └── SimpleSyncHandler.html │ │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ │ └── package-use.html │ │ │ │ │ │ └── threading/ │ │ │ │ │ │ ├── MultiLockDictionary.MultiLock.html │ │ │ │ │ │ ├── MultiLockDictionary.html │ │ │ │ │ │ ├── MultiReadWriteLockDictionary.MultiReadWriteLock.html │ │ │ │ │ │ ├── MultiReadWriteLockDictionary.html │ │ │ │ │ │ ├── class-use/ │ │ │ │ │ │ │ ├── MultiLockDictionary.MultiLock.html │ │ │ │ │ │ │ ├── MultiLockDictionary.html │ │ │ │ │ │ │ ├── MultiReadWriteLockDictionary.MultiReadWriteLock.html │ │ │ │ │ │ │ └── MultiReadWriteLockDictionary.html │ │ │ │ │ │ ├── package-frame.html │ │ │ │ │ │ ├── package-summary.html │ │ │ │ │ │ ├── package-tree.html │ │ │ │ │ │ └── package-use.html │ │ │ │ │ ├── constant-values.html │ │ │ │ │ ├── deprecated-list.html │ │ │ │ │ ├── help-doc.html │ │ │ │ │ ├── index-files/ │ │ │ │ │ │ ├── index-1.html │ │ │ │ │ │ ├── index-10.html │ │ │ │ │ │ ├── index-11.html │ │ │ │ │ │ ├── index-12.html │ │ │ │ │ │ ├── index-13.html │ │ │ │ │ │ ├── index-14.html │ │ │ │ │ │ ├── index-15.html │ │ │ │ │ │ ├── index-16.html │ │ │ │ │ │ ├── index-17.html │ │ │ │ │ │ ├── index-18.html │ │ │ │ │ │ ├── index-19.html │ │ │ │ │ │ ├── index-2.html │ │ │ │ │ │ ├── index-20.html │ │ │ │ │ │ ├── index-21.html │ │ │ │ │ │ ├── index-22.html │ │ │ │ │ │ ├── index-23.html │ │ │ │ │ │ ├── index-24.html │ │ │ │ │ │ ├── index-3.html │ │ │ │ │ │ ├── index-4.html │ │ │ │ │ │ ├── index-5.html │ │ │ │ │ │ ├── index-6.html │ │ │ │ │ │ ├── index-7.html │ │ │ │ │ │ ├── index-8.html │ │ │ │ │ │ └── index-9.html │ │ │ │ │ ├── index.html │ │ │ │ │ ├── overview-frame.html │ │ │ │ │ ├── overview-summary.html │ │ │ │ │ ├── overview-tree.html │ │ │ │ │ ├── package-list │ │ │ │ │ ├── script.js │ │ │ │ │ ├── serialized-form.html │ │ │ │ │ └── stylesheet.css │ │ │ │ ├── proguard-rules.pro │ │ │ │ └── src/ │ │ │ │ └── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── microsoft/ │ │ │ │ │ └── windowsazure/ │ │ │ │ │ └── mobileservices/ │ │ │ │ │ ├── ApiJsonOperationCallback.java │ │ │ │ │ ├── ApiOperationCallback.java │ │ │ │ │ ├── MobileServiceApplication.java │ │ │ │ │ ├── MobileServiceClient.java │ │ │ │ │ ├── MobileServiceException.java │ │ │ │ │ ├── MobileServiceFeatures.java │ │ │ │ │ ├── MobileServiceList.java │ │ │ │ │ ├── ServiceFilterResponseCallback.java │ │ │ │ │ ├── UserAuthenticationCallback.java │ │ │ │ │ ├── authentication/ │ │ │ │ │ │ ├── LoginManager.java │ │ │ │ │ │ ├── MobileServiceAuthenticationProvider.java │ │ │ │ │ │ └── MobileServiceUser.java │ │ │ │ │ ├── http/ │ │ │ │ │ │ ├── AndroidHttpClientFactory.java │ │ │ │ │ │ ├── AndroidHttpClientFactoryImpl.java │ │ │ │ │ │ ├── HttpPatch.java │ │ │ │ │ │ ├── MobileServiceConnection.java │ │ │ │ │ │ ├── MobileServiceHttpClient.java │ │ │ │ │ │ ├── NextServiceFilterCallback.java │ │ │ │ │ │ ├── RequestAsyncTask.java │ │ │ │ │ │ ├── ServiceFilter.java │ │ │ │ │ │ ├── ServiceFilterRequest.java │ │ │ │ │ │ ├── ServiceFilterRequestImpl.java │ │ │ │ │ │ ├── ServiceFilterResponse.java │ │ │ │ │ │ └── ServiceFilterResponseImpl.java │ │ │ │ │ ├── notifications/ │ │ │ │ │ │ ├── GcmNativeRegistration.java │ │ │ │ │ │ ├── GcmTemplateRegistration.java │ │ │ │ │ │ ├── MobileServicePush.java │ │ │ │ │ │ ├── PnsSpecificRegistrationFactory.java │ │ │ │ │ │ ├── Registration.java │ │ │ │ │ │ ├── RegistrationCallback.java │ │ │ │ │ │ ├── RegistrationGoneException.java │ │ │ │ │ │ ├── TemplateRegistration.java │ │ │ │ │ │ ├── TemplateRegistrationCallback.java │ │ │ │ │ │ └── UnregisterCallback.java │ │ │ │ │ ├── table/ │ │ │ │ │ │ ├── DateTimeOffset.java │ │ │ │ │ │ ├── MobileServiceConflictException.java │ │ │ │ │ │ ├── MobileServiceConflictExceptionJson.java │ │ │ │ │ │ ├── MobileServiceExceptionBase.java │ │ │ │ │ │ ├── MobileServiceJsonTable.java │ │ │ │ │ │ ├── MobileServicePreconditionFailedException.java │ │ │ │ │ │ ├── MobileServicePreconditionFailedExceptionJson.java │ │ │ │ │ │ ├── MobileServiceSystemColumns.java │ │ │ │ │ │ ├── MobileServiceSystemProperty.java │ │ │ │ │ │ ├── MobileServiceTable.java │ │ │ │ │ │ ├── MobileServiceTableBase.java │ │ │ │ │ │ ├── MobileServiceTableSystemPropertiesProvider.java │ │ │ │ │ │ ├── TableDeleteCallback.java │ │ │ │ │ │ ├── TableJsonOperationCallback.java │ │ │ │ │ │ ├── TableJsonQueryCallback.java │ │ │ │ │ │ ├── TableOperationCallback.java │ │ │ │ │ │ ├── TableQueryCallback.java │ │ │ │ │ │ ├── query/ │ │ │ │ │ │ │ ├── BinaryOperatorKind.java │ │ │ │ │ │ │ ├── BinaryOperatorNode.java │ │ │ │ │ │ │ ├── BinaryOperatorNodeMerger.java │ │ │ │ │ │ │ ├── ConstantNode.java │ │ │ │ │ │ │ ├── ConstantNodeMerger.java │ │ │ │ │ │ │ ├── ExecutableJsonQuery.java │ │ │ │ │ │ │ ├── ExecutableQuery.java │ │ │ │ │ │ │ ├── FieldNode.java │ │ │ │ │ │ │ ├── FieldNodeMerger.java │ │ │ │ │ │ │ ├── FunctionCallKind.java │ │ │ │ │ │ │ ├── FunctionCallNode.java │ │ │ │ │ │ │ ├── FunctionCallNodeMerger.java │ │ │ │ │ │ │ ├── Query.java │ │ │ │ │ │ │ ├── QueryBase.java │ │ │ │ │ │ │ ├── QueryException.java │ │ │ │ │ │ │ ├── QueryNode.java │ │ │ │ │ │ │ ├── QueryNodeKind.java │ │ │ │ │ │ │ ├── QueryNodeMerger.java │ │ │ │ │ │ │ ├── QueryNodeODataWriter.java │ │ │ │ │ │ │ ├── QueryNodeSQLWriter.java │ │ │ │ │ │ │ ├── QueryNodeVisitor.java │ │ │ │ │ │ │ ├── QueryODataWriter.java │ │ │ │ │ │ │ ├── QueryOperations.java │ │ │ │ │ │ │ ├── QueryOrder.java │ │ │ │ │ │ │ ├── QuerySQLWriter.java │ │ │ │ │ │ │ ├── UnaryOperatorKind.java │ │ │ │ │ │ │ ├── UnaryOperatorNode.java │ │ │ │ │ │ │ └── UnaryOperatorNodeMerger.java │ │ │ │ │ │ ├── serialization/ │ │ │ │ │ │ │ ├── DateSerializer.java │ │ │ │ │ │ │ ├── JsonEntityParser.java │ │ │ │ │ │ │ └── LongSerializer.java │ │ │ │ │ │ └── sync/ │ │ │ │ │ │ ├── MobileServiceJsonSyncTable.java │ │ │ │ │ │ ├── MobileServiceSyncContext.java │ │ │ │ │ │ ├── MobileServiceSyncTable.java │ │ │ │ │ │ ├── localstore/ │ │ │ │ │ │ │ ├── ColumnDataInfo.java │ │ │ │ │ │ │ ├── ColumnDataType.java │ │ │ │ │ │ │ ├── MobileServiceLocalStore.java │ │ │ │ │ │ │ ├── MobileServiceLocalStoreException.java │ │ │ │ │ │ │ └── SQLiteLocalStore.java │ │ │ │ │ │ ├── operations/ │ │ │ │ │ │ │ ├── DeleteOperation.java │ │ │ │ │ │ │ ├── DeleteOperationCollapser.java │ │ │ │ │ │ │ ├── InsertOperation.java │ │ │ │ │ │ │ ├── InsertOperationCollapser.java │ │ │ │ │ │ │ ├── LocalTableOperationProcessor.java │ │ │ │ │ │ │ ├── MobileServiceTableOperationState.java │ │ │ │ │ │ │ ├── RemoteTableOperationProcessor.java │ │ │ │ │ │ │ ├── TableOperation.java │ │ │ │ │ │ │ ├── TableOperationCollapser.java │ │ │ │ │ │ │ ├── TableOperationError.java │ │ │ │ │ │ │ ├── TableOperationKind.java │ │ │ │ │ │ │ ├── TableOperationVisitor.java │ │ │ │ │ │ │ ├── UpdateOperation.java │ │ │ │ │ │ │ └── UpdateOperationCollapser.java │ │ │ │ │ │ ├── pull/ │ │ │ │ │ │ │ ├── IncrementalPullStrategy.java │ │ │ │ │ │ │ └── PullStrategy.java │ │ │ │ │ │ ├── push/ │ │ │ │ │ │ │ ├── MobileServicePushCompletionResult.java │ │ │ │ │ │ │ ├── MobileServicePushFailedException.java │ │ │ │ │ │ │ └── MobileServicePushStatus.java │ │ │ │ │ │ ├── queue/ │ │ │ │ │ │ │ ├── OperationErrorList.java │ │ │ │ │ │ │ └── OperationQueue.java │ │ │ │ │ │ └── synchandler/ │ │ │ │ │ │ ├── MobileServiceSyncHandler.java │ │ │ │ │ │ ├── MobileServiceSyncHandlerException.java │ │ │ │ │ │ └── SimpleSyncHandler.java │ │ │ │ │ └── threading/ │ │ │ │ │ ├── MultiLockDictionary.java │ │ │ │ │ └── MultiReadWriteLockDictionary.java │ │ │ │ └── res/ │ │ │ │ └── values/ │ │ │ │ └── strings.xml │ │ │ └── tools/ │ │ │ ├── License.rtf │ │ │ ├── package.bat │ │ │ └── thirdpartynotices.rtf │ │ └── test/ │ │ └── sdk.testapp/ │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── microsoft/ │ │ │ └── windowsazure/ │ │ │ └── mobileservices/ │ │ │ └── sdk/ │ │ │ └── testapp/ │ │ │ ├── framework/ │ │ │ │ ├── filters/ │ │ │ │ │ ├── EchoFilter.java │ │ │ │ │ ├── HttpMetaEchoFilter.java │ │ │ │ │ ├── NullResponseContentFilter.java │ │ │ │ │ ├── NullResponseFilter.java │ │ │ │ │ ├── ServiceFilterRequestMock.java │ │ │ │ │ ├── ServiceFilterResponseMock.java │ │ │ │ │ └── StatusLineMock.java │ │ │ │ └── mocks/ │ │ │ │ ├── MobileServiceLocalStoreMock.java │ │ │ │ └── MobileServiceSyncHandlerMock.java │ │ │ └── test/ │ │ │ ├── CustomApiClientTests.java │ │ │ ├── EnhancedPushTests.java │ │ │ ├── IdPropertyTests.java │ │ │ ├── LoginTests.java │ │ │ ├── MobileServiceClientTests.java │ │ │ ├── MobileServiceFeaturesTests.java │ │ │ ├── MobileServiceQueryTests.java │ │ │ ├── MobileServiceSyncTableTests.java │ │ │ ├── MobileServiceTableTests.java │ │ │ ├── MobileServiceUserTests.java │ │ │ ├── SQLiteStoreQueryTests.java │ │ │ ├── SQLiteStoreTests.java │ │ │ ├── SerializationTests.java │ │ │ ├── ServiceFilterTests.java │ │ │ ├── SystemPropertiesTests.java │ │ │ ├── URLTests.java │ │ │ ├── helpers/ │ │ │ │ ├── EncodingUtilities.java │ │ │ │ └── SQLiteStoreTestsUtilities.java │ │ │ └── types/ │ │ │ ├── Address.java │ │ │ ├── ComplexPersonTestObject.java │ │ │ ├── CustomFunctionOneParameter.java │ │ │ ├── CustomFunctionTwoParameters.java │ │ │ ├── DateTestObject.java │ │ │ ├── IdPropertyTestClasses.java │ │ │ ├── PersonTestObject.java │ │ │ ├── PersonTestObjectWithStringId.java │ │ │ ├── PersonTestObjectWithoutId.java │ │ │ ├── ResultsContainer.java │ │ │ ├── SystemPropertyTestClasses.java │ │ │ └── data/ │ │ │ ├── IdTestData.java │ │ │ └── SystemPropertiesTestData.java │ │ └── res/ │ │ └── values/ │ │ └── strings.xml │ └── iOS/ │ ├── .gitignore │ ├── License.rtf │ ├── WindowsAzureMobileServices.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ ├── AzureMobileServicesFunctionalTests.xcscheme │ │ ├── WindowsAzureMobileServices.xcscheme │ │ └── WindowsAzureMobileServicesFramework.xcscheme │ ├── appledoc/ │ │ └── AppledocSettings.plist │ ├── build.command │ ├── doxygen.config │ ├── src/ │ │ ├── Info.plist │ │ ├── MSAPIConnection.h │ │ ├── MSAPIConnection.m │ │ ├── MSAPIRequest.h │ │ ├── MSAPIRequest.m │ │ ├── MSClient.h │ │ ├── MSClient.m │ │ ├── MSClientConnection.h │ │ ├── MSClientConnection.m │ │ ├── MSClientInternal.h │ │ ├── MSCoreDataStore.h │ │ ├── MSCoreDataStore.m │ │ ├── MSDateOffset.h │ │ ├── MSDateOffset.m │ │ ├── MSError.h │ │ ├── MSError.m │ │ ├── MSFilter.h │ │ ├── MSJSONSerializer.h │ │ ├── MSJSONSerializer.m │ │ ├── MSLocalStorage.h │ │ ├── MSLocalStorage.m │ │ ├── MSLogin.h │ │ ├── MSLogin.m │ │ ├── MSLoginController.h │ │ ├── MSLoginController.m │ │ ├── MSLoginSerializer.h │ │ ├── MSLoginSerializer.m │ │ ├── MSLoginView.h │ │ ├── MSLoginView.m │ │ ├── MSNaiveISODateFormatter.h │ │ ├── MSNaiveISODateFormatter.m │ │ ├── MSOperationQueue.h │ │ ├── MSOperationQueue.m │ │ ├── MSPredicateTranslator.h │ │ ├── MSPredicateTranslator.m │ │ ├── MSPullSettings.h │ │ ├── MSPullSettings.m │ │ ├── MSPullSettingsInternal.h │ │ ├── MSPush.h │ │ ├── MSPush.m │ │ ├── MSPushHttp.h │ │ ├── MSPushHttp.m │ │ ├── MSPushRequest.h │ │ ├── MSPushRequest.m │ │ ├── MSQuery.h │ │ ├── MSQuery.m │ │ ├── MSQueryInternal.h │ │ ├── MSQueryResult.h │ │ ├── MSQueryResult.m │ │ ├── MSQueuePullOperation.h │ │ ├── MSQueuePullOperation.m │ │ ├── MSQueuePullOperationInternal.h │ │ ├── MSQueuePurgeOperation.h │ │ ├── MSQueuePurgeOperation.m │ │ ├── MSQueuePushOperation.h │ │ ├── MSQueuePushOperation.m │ │ ├── MSRegistrationManager.h │ │ ├── MSRegistrationManager.m │ │ ├── MSSDKFeatures.h │ │ ├── MSSDKFeatures.m │ │ ├── MSSerializer.h │ │ ├── MSSyncContext.h │ │ ├── MSSyncContext.m │ │ ├── MSSyncContextInternal.h │ │ ├── MSSyncContextReadResult.h │ │ ├── MSSyncContextReadResult.m │ │ ├── MSSyncTable.h │ │ ├── MSSyncTable.m │ │ ├── MSTable.h │ │ ├── MSTable.m │ │ ├── MSTableConfigValue.h │ │ ├── MSTableConfigValue.m │ │ ├── MSTableConnection.h │ │ ├── MSTableConnection.m │ │ ├── MSTableInternal.h │ │ ├── MSTableOperation.h │ │ ├── MSTableOperation.m │ │ ├── MSTableOperationError.h │ │ ├── MSTableOperationError.m │ │ ├── MSTableOperationInternal.h │ │ ├── MSTableRequest.h │ │ ├── MSTableRequest.m │ │ ├── MSURLBuilder.h │ │ ├── MSURLBuilder.m │ │ ├── MSUser.h │ │ ├── MSUser.m │ │ ├── MSUserAgentBuilder.h │ │ ├── MSUserAgentBuilder.m │ │ ├── WindowsAzureMobileServices-Prefix.pch │ │ └── WindowsAzureMobileServices.h │ └── test/ │ ├── CoreDataTestModel.xcdatamodeld/ │ │ └── CoreDataTestModel.xcdatamodel/ │ │ └── contents │ ├── MSClientTests.m │ ├── MSCoreDataStore+TestHelper.h │ ├── MSCoreDataStore+TestHelper.m │ ├── MSCoreDataStoreTests.m │ ├── MSFilterTest.m │ ├── MSJSONSerializerTests.m │ ├── MSLocalStorageTests.m │ ├── MSMultiRequestTestFilter.h │ ├── MSMultiRequestTestFilter.m │ ├── MSOfflinePassthroughHelper.h │ ├── MSOfflinePassthroughHelper.m │ ├── MSPredicateTranslatorTests.m │ ├── MSPullSettingsTests.m │ ├── MSPushHttpTests.m │ ├── MSPushTests.m │ ├── MSQueryTests.m │ ├── MSSyncTableTests.m │ ├── MSTable+MSTableTestUtilities.h │ ├── MSTable+MSTableTestUtilities.m │ ├── MSTableFuncTests.m │ ├── MSTableOperationErrorTests.m │ ├── MSTableOperationTests.m │ ├── MSTableTests.m │ ├── MSTestFilter.h │ ├── MSTestFilter.m │ ├── MSTestWaiter.h │ ├── MSTestWaiter.m │ ├── MSURLbuilderTests.m │ ├── MSUserAgentBuilderTests.m │ ├── MSUserTests.m │ ├── TodoItem.h │ ├── TodoItem.m │ ├── WindowsAzureMobileServicesFunctionalTests.m │ ├── WindowsAzureMobileServicesTests-Info.plist │ ├── en.lproj/ │ │ └── InfoPlist.strings │ └── settings.plist └── test/ ├── .gitignore ├── Android/ │ ├── Automation/ │ │ └── AndroidAutomation.bat │ └── ZumoE2ETestApp/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ ├── getLibs.ps1 │ │ ├── getLibs.sh │ │ └── required-libs.txt │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── microsoft/ │ │ └── windowsazure/ │ │ └── mobileservices/ │ │ └── zumoe2etestapp/ │ │ ├── Constants.java │ │ ├── GCMIntentService.java │ │ ├── MainActivity.java │ │ ├── TestCaseAdapter.java │ │ ├── ZumoPreferenceActivity.java │ │ ├── framework/ │ │ │ ├── CompositeTestGroup.java │ │ │ ├── ExpectedValueException.java │ │ │ ├── FroyoAndroidHttpClientFactory.java │ │ │ ├── FroyoSupport.java │ │ │ ├── LogServiceFilter.java │ │ │ ├── TestCase.java │ │ │ ├── TestExecutionCallback.java │ │ │ ├── TestGroup.java │ │ │ ├── TestResult.java │ │ │ ├── TestStatus.java │ │ │ ├── Util.java │ │ │ └── log/ │ │ │ └── DaylightLogger.java │ │ ├── push/ │ │ │ ├── GCMMessageCallback.java │ │ │ ├── GCMMessageHelper.java │ │ │ ├── GCMMessageManager.java │ │ │ └── GCMRegistrationMessage.java │ │ └── tests/ │ │ ├── ClientSDKLoginTests.java │ │ ├── CustomApiTests.java │ │ ├── EnhancedPushTests.java │ │ ├── LoginTests.java │ │ ├── MiscTests.java │ │ ├── MockResponse.java │ │ ├── MultipleRequestFilter.java │ │ ├── OfflineTests.java │ │ ├── PushTests.java │ │ ├── QueryTestData.java │ │ ├── QueryTests.java │ │ ├── RemoveAuthenticationServiceFilter.java │ │ ├── RoundTripTests.java │ │ ├── StringIdTests.java │ │ ├── SystemPropertiesTests.java │ │ ├── UpdateDeleteTests.java │ │ └── types/ │ │ ├── AggregateException.java │ │ ├── AllIntIdMovies.java │ │ ├── AllStringIdMovies.java │ │ ├── ComplexType.java │ │ ├── ComplexType2.java │ │ ├── EnumType.java │ │ ├── FilterResult.java │ │ ├── IntIdMovie.java │ │ ├── IntIdRoundTripTableElement.java │ │ ├── ListFilter.java │ │ ├── Movie.java │ │ ├── MovieComparator.java │ │ ├── ParamsTestTableItem.java │ │ ├── RoundTripTableElement.java │ │ ├── SimpleFilter.java │ │ ├── SimpleMovieFilter.java │ │ ├── StringIdJsonElement.java │ │ ├── StringIdMovie.java │ │ ├── StringIdRoundTripTableElement.java │ │ ├── StringIdRoundTripTableSoftDeleteElement.java │ │ ├── StringIdTableItem.java │ │ └── SystemPropertiesTestData.java │ └── res/ │ ├── layout/ │ │ ├── activity_main.xml │ │ └── row_list_test_case.xml │ ├── menu/ │ │ └── activity_main.xml │ ├── raw/ │ │ └── mobileservicestore.bks │ ├── values/ │ │ ├── strings.xml │ │ ├── strings_activity_zumo_preference.xml │ │ └── styles.xml │ ├── values-v11/ │ │ └── styles.xml │ ├── values-v14/ │ │ └── styles.xml │ └── xml/ │ └── pref_general.xml ├── JavaScript/ │ ├── .gitignore │ ├── TestFramework/ │ │ ├── css/ │ │ │ └── default.css │ │ └── js/ │ │ ├── testFramework.js │ │ ├── testFrameworkConnections.js │ │ └── tests/ │ │ ├── allTests.js │ │ ├── apiTests.js │ │ ├── loginTests.js │ │ ├── miscTests.js │ │ ├── pushTests.js │ │ ├── queryTestData.js │ │ ├── queryTests.js │ │ ├── roundTripTests.js │ │ └── updateDeleteTests.js │ └── ZumoE2ETestAppJs/ │ ├── .gitignore │ ├── ZumoE2ETestAppHTML/ │ │ ├── .gitignore │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Web.Debug.config │ │ ├── Web.Release.config │ │ ├── Web.config │ │ ├── ZumoE2ETestAppHTML.csproj │ │ ├── default.html │ │ └── js/ │ │ ├── lib/ │ │ │ └── MobileServices.Web.js │ │ └── platformSpecificFunctions.js │ ├── ZumoE2ETestAppJs/ │ │ ├── Package.StoreAssociation.xml │ │ ├── ZumoE2ETestAppJs.jsproj │ │ ├── ZumoE2ETestAppJs_StoreKey.pfx │ │ ├── ZumoE2ETestAppJs_TemporaryKey.pfx │ │ ├── css/ │ │ │ └── where.txt │ │ ├── default.html │ │ ├── js/ │ │ │ ├── MobileServices.intellisense.js │ │ │ ├── MobileServices.js │ │ │ ├── MobileServices.pri │ │ │ ├── default.js │ │ │ ├── platformSpecificFunctions.js │ │ │ ├── tests/ │ │ │ │ └── where.txt │ │ │ └── wl.js │ │ ├── package.appxmanifest │ │ └── packages.config │ └── ZumoE2ETestAppJs.sln ├── PLib/ │ ├── .gitignore │ └── ZumoE2ETestApp/ │ ├── .gitignore │ ├── .nuget/ │ │ ├── NuGet.Config │ │ └── NuGet.targets │ ├── UpgradeLog.htm │ ├── ZumoE2ETestApp/ │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── Common/ │ │ │ └── StandardStyles.xaml │ │ ├── Framework/ │ │ │ ├── TestStatus.cs │ │ │ ├── Util.cs │ │ │ ├── ZumoPushGlobals.cs │ │ │ ├── ZumoTest.cs │ │ │ ├── ZumoTestEventArgs.cs │ │ │ ├── ZumoTestGlobals.cs │ │ │ ├── ZumoTestGroup.cs │ │ │ └── ZumoTestGroupEventArgs.cs │ │ ├── MainPage.xaml │ │ ├── MainPage.xaml.cs │ │ ├── Package.StoreAssociation.xml │ │ ├── Package.appxmanifest │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Tests/ │ │ │ ├── ExceptionTypeWhichWillNeverBeThrown.cs │ │ │ ├── TestStore.cs │ │ │ ├── Types/ │ │ │ │ ├── ICloneableItem.cs │ │ │ │ ├── Movie.cs │ │ │ │ ├── OfflineReadyItem.cs │ │ │ │ ├── OfflineReadyItemNoVersion.cs │ │ │ │ ├── RoundTripTableItem.cs │ │ │ │ ├── StringIdRoundTripTableItem.cs │ │ │ │ └── VersionedType.cs │ │ │ ├── ZumoCUDTests.cs │ │ │ ├── ZumoCustomApiTests.cs │ │ │ ├── ZumoLoginTests.cs │ │ │ ├── ZumoMiscTests.cs │ │ │ ├── ZumoOfflineTests.cs │ │ │ ├── ZumoPushTests.cs │ │ │ ├── ZumoQueryTestData.cs │ │ │ ├── ZumoQueryTests.cs │ │ │ ├── ZumoRoundTripTests.cs │ │ │ ├── ZumoSetupTests.cs │ │ │ └── ZumoTestCommon.cs │ │ ├── UIElements/ │ │ │ ├── AppInfoRepository.cs │ │ │ ├── InputDialog.xaml │ │ │ ├── InputDialog.xaml.cs │ │ │ ├── ListViewForTest.cs │ │ │ ├── ListViewForTestGroup.cs │ │ │ ├── MoviesDisplayControl.xaml │ │ │ ├── MoviesDisplayControl.xaml.cs │ │ │ ├── SaveAppsControl.xaml │ │ │ ├── SaveAppsControl.xaml.cs │ │ │ ├── UploadLogsControl.xaml │ │ │ └── UploadLogsControl.xaml.cs │ │ ├── ZumoE2ETestApp.csproj │ │ ├── ZumoE2ETestApp_StoreKey.pfx │ │ ├── ZumoE2ETestApp_TemporaryKey.pfx │ │ └── packages.config │ ├── ZumoE2ETestApp.sln │ ├── ZumoE2ETestAppNet45/ │ │ ├── App.config │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── Common/ │ │ │ └── StandardStyles.xaml │ │ ├── MainPage.xaml │ │ ├── MainPage.xaml.cs │ │ ├── MainWindow.xaml │ │ ├── MainWindow.xaml.cs │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.resx │ │ │ ├── Settings.Designer.cs │ │ │ └── Settings.settings │ │ ├── UIElements/ │ │ │ ├── InputDialog.xaml │ │ │ ├── InputDialog.xaml.cs │ │ │ ├── MoviesDisplayControl.xaml │ │ │ ├── MoviesDisplayControl.xaml.cs │ │ │ ├── SaveAppsControl.xaml │ │ │ ├── SaveAppsControl.xaml.cs │ │ │ ├── UploadLogsControl.xaml │ │ │ └── UploadLogsControl.xaml.cs │ │ ├── ZumoE2ETestAppNet45.csproj │ │ └── packages.config │ └── ZumoE2ETestAppWP8/ │ ├── App.xaml │ ├── App.xaml.cs │ ├── Framework/ │ │ └── Readme.txt │ ├── LocalizedStrings.cs │ ├── MainPage.xaml │ ├── MainPage.xaml.cs │ ├── Properties/ │ │ ├── AppManifest.xml │ │ ├── AssemblyInfo.cs │ │ └── WMAppManifest.xml │ ├── Resources/ │ │ ├── AppResources.Designer.cs │ │ └── AppResources.resx │ ├── Tests/ │ │ ├── Types/ │ │ │ └── Readme.txt │ │ └── ZumoWP8PushTests.cs │ ├── UIElements/ │ │ ├── InputDialog.xaml │ │ └── InputDialog.xaml.cs │ ├── ZumoE2ETestAppWP8.csproj │ ├── app.config │ └── packages.config ├── SetupScripts/ │ ├── apis/ │ │ ├── SetupAPIs.bat │ │ ├── SetupAPIs.sh │ │ ├── admin.js │ │ ├── admin.json │ │ ├── application.js │ │ ├── application.json │ │ ├── movieFinder.js │ │ ├── movieFinder.json │ │ ├── public.js │ │ ├── public.json │ │ ├── runtimeInfo.js │ │ ├── runtimeInfo.json │ │ ├── shared.js │ │ ├── shared.json │ │ ├── user.js │ │ └── user.json │ └── tables/ │ ├── ParamsTestTable.delete.js │ ├── ParamsTestTable.insert.js │ ├── ParamsTestTable.read.js │ ├── ParamsTestTable.update.js │ ├── SetupTables.bat │ ├── SetupTables.sh │ ├── authenticated.delete.js │ ├── authenticated.insert.js │ ├── authenticated.read.js │ ├── authenticated.update.js │ ├── blog_comments.insert.js │ ├── blog_comments.read.js │ ├── bothIdTypeMovies.insert.js │ ├── droidPushTest.insert.js │ ├── droidRoundTripTable.insert.js │ ├── droidRoundTripTable.read.js │ ├── droidRoundTripTable.update.js │ ├── iosPushTest.insert.js │ ├── iosRoundTripTable.insert.js │ ├── offlinereadyitemnoversion.insert.js │ ├── stringIdRoundTripTable.insert.js │ ├── stringIdRoundTripTable.read.js │ ├── stringIdRoundTripTable.update.js │ ├── w8PushTest.insert.js │ ├── w8RoundTripTable.insert.js │ ├── w8RoundTripTable.read.js │ ├── w8RoundTripTable.update.js │ ├── w8jsRoundTripTable.insert.js │ ├── w8jsRoundTripTable.read.js │ ├── w8jsRoundTripTable.update.js │ ├── w8jsServerQueryMovies.read.js │ └── wp8PushTest.insert.js ├── ZumoE2EServerApp/ │ ├── App_Start/ │ │ └── WebApiConfig.cs │ ├── Controllers/ │ │ ├── AdminController.cs │ │ ├── ApplicationApiController.cs │ │ ├── ApplicationController.cs │ │ ├── AuthenticatedController.cs │ │ ├── MovieFinderApiController.cs │ │ ├── OfflineReadyController.cs │ │ ├── ParamsTestTableController.cs │ │ ├── PublicController.cs │ │ ├── RuntimeInfoController.cs │ │ ├── StringIdMoviesController.cs │ │ ├── StringIdRoundTripTableController.cs │ │ ├── W8JSRoundTripTableController.cs │ │ ├── W8PushTestController.cs │ │ └── W8RoundTripTableController.cs │ ├── DataObjects/ │ │ ├── Movie.cs │ │ ├── OfflineReady.cs │ │ ├── ParamsTestTableEntity.cs │ │ ├── RoundTripTableItem.cs │ │ ├── StringIdRoundTripTableItem.cs │ │ ├── TestUser.cs │ │ ├── W8JSRoundTripTableItem.cs │ │ └── W8PushTestEntity.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Models/ │ │ └── SDKClientTestContext.cs │ ├── Performance/ │ │ └── Microsoft.ServiceBus.MessagingPerformanceCounters.man │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── ScheduledJobs/ │ │ └── SimpleJob.cs │ ├── Utils/ │ │ ├── ComplexTypeDomainManager.cs │ │ ├── CustomSharedApi.cs │ │ ├── InMemoryDomainManager.cs │ │ ├── StringIdRoundTripDomainManager.cs │ │ ├── W8JSRoundTripDomainManager.cs │ │ └── W8RoundTripDomainManager.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ ├── ZumoE2EServerApp.csproj │ ├── ZumoE2EServerApp.sln │ └── packages.config └── iOS/ ├── .gitignore └── ZumoE2ETestApp/ ├── WindowsAzureMobileServices.framework/ │ ├── Headers/ │ │ ├── MSClient.h │ │ ├── MSError.h │ │ ├── MSFilter.h │ │ ├── MSLoginController.h │ │ ├── MSPush.h │ │ ├── MSQuery.h │ │ ├── MSTable.h │ │ ├── MSUser.h │ │ └── WindowsAzureMobileServices.h │ ├── Versions/ │ │ ├── A/ │ │ │ ├── Headers/ │ │ │ │ ├── MSClient.h │ │ │ │ ├── MSError.h │ │ │ │ ├── MSFilter.h │ │ │ │ ├── MSLoginController.h │ │ │ │ ├── MSPush.h │ │ │ │ ├── MSQuery.h │ │ │ │ ├── MSTable.h │ │ │ │ ├── MSUser.h │ │ │ │ └── WindowsAzureMobileServices.h │ │ │ └── WindowsAzureMobileServices │ │ └── Current/ │ │ └── Headers/ │ │ ├── MSClient.h │ │ ├── MSError.h │ │ ├── MSFilter.h │ │ ├── MSLoginController.h │ │ ├── MSPush.h │ │ ├── MSQuery.h │ │ ├── MSTable.h │ │ ├── MSUser.h │ │ └── WindowsAzureMobileServices.h │ ├── WindowsAzureMobileServices │ └── license.rtf ├── ZumoE2ETestApp/ │ ├── TestLoggingFilter.h │ ├── TestLoggingFilter.m │ ├── ZumoAppDelegate.h │ ├── ZumoAppDelegate.m │ ├── ZumoCUDTests.h │ ├── ZumoCUDTests.m │ ├── ZumoCustomApiTests.h │ ├── ZumoCustomApiTests.m │ ├── ZumoE2ETestApp-Info.plist │ ├── ZumoE2ETestApp-Prefix.pch │ ├── ZumoLogUpdater.h │ ├── ZumoLogUpdater.m │ ├── ZumoLoginTests.h │ ├── ZumoLoginTests.m │ ├── ZumoMainTableHeader.xib │ ├── ZumoMainTableViewController.h │ ├── ZumoMainTableViewController.m │ ├── ZumoMiscTests.h │ ├── ZumoMiscTests.m │ ├── ZumoPushTests.h │ ├── ZumoPushTests.m │ ├── ZumoQueryTestData.h │ ├── ZumoQueryTestData.m │ ├── ZumoQueryTests.h │ ├── ZumoQueryTests.m │ ├── ZumoRoundTripTests.h │ ├── ZumoRoundTripTests.m │ ├── ZumoSavedAppsHeader.xib │ ├── ZumoSavedAppsTableViewController.h │ ├── ZumoSavedAppsTableViewController.m │ ├── ZumoTest.h │ ├── ZumoTest.m │ ├── ZumoTestCallbacks.h │ ├── ZumoTestGlobals.h │ ├── ZumoTestGlobals.m │ ├── ZumoTestGroup.h │ ├── ZumoTestGroup.m │ ├── ZumoTestGroupCallbacks.h │ ├── ZumoTestGroupTableHeader.xib │ ├── ZumoTestGroupTableViewController.h │ ├── ZumoTestGroupTableViewController.m │ ├── ZumoTestHelpViewController.h │ ├── ZumoTestHelpViewController.m │ ├── ZumoTestHelpViewController.xib │ ├── ZumoTestRunSetup.h │ ├── ZumoTestRunSetup.m │ ├── ZumoTestStore.h │ ├── ZumoTestStore.m │ ├── en.lproj/ │ │ └── InfoPlist.strings │ └── main.m └── ZumoE2ETestApp.xcodeproj/ └── project.pbxproj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain ================================================ FILE: .gitignore ================================================ .DS_Store bin gen target *.keystore *.swp *.orig *.log *.userprefs seed.txt map.txt .metadata sdk/android/src/notifications-handler/libs/google-play-services.jar sdk/android/src/sdk/libs/gson-2.2.2.jar sdk/android/src/sdk/libs/guava-16.0.1.jar sdk/android/test/sdk.testapp.tests/libs/gson-2.2.2.jar *.mdf *.ldf obj *.csproj.user test/ZumoE2EServerApp/Properties/PublishProfiles/ sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Net45.Test/sqlite3.dll StyleCop.Cache packages *.suo *.jar *.xccheckout deriveddata *.iml ================================================ FILE: .gitmodules ================================================ [submodule "sdk/Javascript/src/External/esprima"] path = sdk/Javascript/src/External/esprima url = https://github.com/ariya/esprima.git [submodule "sdk/Javascript/src/External/queryjs"] path = sdk/Javascript/src/External/queryjs url = https://github.com/WindowsAzure/queryjs.git [submodule "sdk/Managed/src/External/Xamarin.Auth"] path = sdk/Managed/src/External/Xamarin.Auth url = https://github.com/azure/azure-mobile-services-xamarin-auth.git ================================================ FILE: CHANGELOG.android.md ================================================ # Azure Mobile Services Android SDK Change Log ### Android SDK: Version 2.0.3 - Fix in the incremental sync query building logic [#720](https://github.com/Azure/azure-mobile-services/issues/720) to use correct parentheses and skip logic [c58d4e8](https://github.com/Azure/azure-mobile-services/commit/c58d4e8) - Fix for the deserialization bug [#718](https://github.com/Azure/azure-mobile-services/issues/718) on OperationErrorList table with datatype mismatch [15e9f9b](https://github.com/Azure/azure-mobile-services/commit/15e9f9b) ### Android SDK: Version 2.0.2 - Support for operation state tracking - Fix for type incompatibility from datetime to datetimeoffset - Methods added for CancelAndDiscard and CancelAndUpdate for customized conflict handling - Fix for the local store database connection issue caused due to race condition in asynchronous operations - Updated end to end test - Support for binary data type on queries (for instance to query using the __version column) - Improvements on the DateSerializer ### Android SDK: Version 2.0.2-Beta2 - Fix for the pull action with SQLite ### Android SDK: Version 2.0.2-Beta - Mobile Services SDK version indicator with user-agent - Introduced conflict exception to facilitate handling of 409 conflict - Fix for the cropped UI for Facebook authentication - Fix for SQL ordering issue - Support for Incremental Sync to pull data with flexibility - Support for Soft Delete - Improved SQLite insert performance on pull - Purge no longer pushes, instead it throws an exception as user expects - Local item deletion exception is handled properly according to server state - Support for continuation tokens in queries for paging - InsertAsync throws expected exception on duplicate insert - Fix for Local store & SQLite missing/mismatch table columns (i.e. “_deleted”) - Support for Android Studio - Send custom query string parameters with loginAsync Android ### Android SDK: Version 1.1.5 - Added support for Windows Azure Notification Hub integration ### Android SDK: Version 1.1.3 - Support for optimistic concurrency (version / ETag) validation - Support for `__createdAt` / `__updatedAt` table columns - Added support for the Windows Azure Active Directory authentication in the `MobileServiceAuthenticationProvider` enum. - Also added a mapping from that name to the value used in the service REST API (`/login/aad`) ### Android SDK: Version 1.1.0 - Support for tables with string ids - Overload for log in which takes the provider as a string, in addition to the one with enums ================================================ FILE: CHANGELOG.ios.md ================================================ # Azure Mobile Services iOS SDK Change Log ## SDK Downloads - [Latest iOS 2.x SDK](http://aka.ms/gc6fex) - **requires XCode 7** - [iOS 1.2.4 SDK](http://aka.ms/kymw2g) ### Version 2.2.2 - Updated build tooling to use XCode 7 and include BitCode [issue #794](https://github.com/Azure/azure-mobile-services/issues/794) - Note: Framework now requires using XCode 7 - Fixed issue with MSCoreDataStore.h that prevented it from bring used as part of a cocoapod ### Version 2.2.1 - Fixed [issue #768](https://github.com/Azure/azure-mobile-services/issues/768) that was causing a memory leak when using NSURLSession [204f210](https://github.com/Azure/azure-mobile-services/commit/204f210) - Fix NSNull templateName [9347390](https://github.com/Azure/azure-mobile-services/commit/9347390) ### Version 2.2.0 - Updated offline sync logic to do a case-insensitive comparison on the ID column [328aadf](https://github.com/Azure/azure-mobile-services/commit/328aadf) - Added support for returning the underlying NSOperation when performing push, pull and purge operations [7c37f60](https://github.com/Azure/azure-mobile-services/commit/7c37f60) - Added support for configuring the page size of a pull operation [0c31aa3](https://github.com/Azure/azure-mobile-services/commit/0c31aa3) - Added support for NSUUID in PredicateTranslator [24c5a61](https://github.com/Azure/azure-mobile-services/commit/24c5a61) - Fixed [issue #699] (https://github.com/Azure/azure-mobile-services/issues/699) that prevented properties with value nil from being sent to server [bf41081](https://github.com/Azure/azure-mobile-services/commit/bf41081) - Fixed handling of network errors during push operations [1a9fdf4](https://github.com/Azure/azure-mobile-services/commit/1a9fdf4) - Fixed potential race conditions while performing table operations [15581be](https://github.com/Azure/azure-mobile-services/commit/15581be) - Fixed incorrect ID validation during insert operations [f5e44d4](https://github.com/Azure/azure-mobile-services/commit/f5e44d4) ### Version 2.1.0 - Fix cancelAndUpdate and cancelAndDiscard actions on the MSTableOperationError class - Fix issues with sync operations not firing their completion blocks on the correct queue ### Version 2.0.0 - GA of offline sync changes from previous betas - Now requires iOS 7 or newer to use ### Version 2.0.0 beta2 - **[Breaking]** Changed MSReadQueryBlock to return MSQueryResult instead of items and totalCount. See [this blog post](http://azure.microsoft.com/blog/2014/10/07/mobile-services-beta-ios-sdk-released/) for more information. - Added support for incremental sync - Added support for query parameters in pull operations - Added support for following link headers returned from the .NET backend - Fixed issue with login controller completing before animation completes - Added a method for force purge of local data - Added a helper method to return an NSDictionary from an NSManagedObject - Fixed issue with the __includeDeleted flag sending the wrong value ### Version 2.0.0 beta1 - Added support for incremental sync - Added support for query parameters in pull operations - Fixed issue with login controller completing before animation completes - Added a method for force purge of local data - Added a helper method to return an NSDictionary from an NSManagedObject - Fixed issue with the __includeDeleted flag sending the wrong value ### Version 1.3 alpha1 - Added support for offline and sync ### Version 1.2.4 - Address bug where version property was returned to the caller even when not asked for - Fixes Swift QS for syntax changes up to Xcode Beta 7 ### Version 1.2.3 - Fix issue with const when using both Azure Messaging and Mobile Services frameworks - Fix issue [#306](https://github.com/Azure/azure-mobile-services/issues/306) with how arrays passed as query string params to table and custom APIs are converted - Fix issue where system properties (__version, __updatedAt, etc) were returned to the caller when they were not requested ### Version 1.2.2 - Added support for APNS Azure Notification Hub integration - Support for optimistic concurrency on delete ### iOS SDK - - Fix issue [#218](https://github.com/WindowsAzure/azure-mobile-services/issues/218) in which some dates coming from the mobile services with the .NET runtime weren't parsed correctly ### Version 1.1.3 - Added a mapping in the authentication provider from WindowsAzureActiveDirectory to the value used in the REST API (`/login/aad`) ### Version 1.1.2 - Supports the arm64 architecture - Now requires iOS 6 or newer to use ### Version 1.1.1 - Support for optimistic concurrency (version / ETag) validation - Support for `__createdAt` / `__updatedAt` table columns - Fix bug with using arrays in invokeAPI ### Version 1.1.0 - Support for tables with string ids ================================================ FILE: CHANGELOG.javascript.md ================================================ # Azure Mobile Services JavaScript SDK Change Log ### JavaScript SDK: Version 1.2.7 - Added support for phonegap/cordova with [plugin repo](https://github.com/Azure/azure-mobile-services-cordova) ### JavaScript SDK: Version 1.2.5 - Added support for sending provider specific query string parameters in login using new loginWithOptions method - Added support for registering devices with notification hubs for apns and gcm - Fixed issue with InAppBrowser on iOS devices during auth workflows when using Cordova/PhoneGap ### JavaScript SDK: Version 1.2.4 - Fixed crash when server response did not have a Content-Type header ### JavaScript SDK: Version 1.2.2 - Support for optimistic concurrency on delete ### JavaScript SDK: Version 1.1.5 - Fix issue [#218](https://github.com/WindowsAzure/azure-mobile-services/issues/218) in which some dates coming from the mobile services with the .NET runtime weren't parsed correctly - [WinJS only] Fix race condition on notification hub integration initialization when storage was corrupted ### JavaScript SDK: Version 1.1.4 - Added support for Windows Azure Notification Hub integration for WinJS. ### JavaScript SDK: Version 1.1.3 - Added a mapping in the authentication provider from WindowsAzureActiveDirectory to the value used in the REST API (`/login/aad`) ### JavaScript SDK: Version 1.1.2 - Support for optimistic concurrency (version / ETag) validation - Support for `__createdAt` / `__updatedAt` table columns ### JavaScript SDK: Version 1.1.0 - Support for tables with string ids - Removed client restriction on valid providers for login - Files are now served from http://ajax.aspnetcdn.com/ajax/mobileservices/MobileServices.Web-[version].min.js (or [version].js for the non minified copy) ### JavaScript SDK: Version 1.0.3: - Added support for `String.substr` inside functions on `where` clauses - Fix [#152](https://github.com/WindowsAzure/azure-mobile-services/issues/152) - InvokeApi method crashes on IE9 and IE8 - Fixed issue with login popup not being closed when using IE11 ================================================ FILE: CHANGELOG.managed.md ================================================ # Azure Mobile Services Managed SDK Change Log ### Managed SDK: Version 1.3.2 - Added workaround for WinRT issue [#658](https://github.com/WindowsAzure/azure-mobile-services/issues/658) by removing localization in SQLiteStore and in the SDK [6af8b30](https://github.com/Azure/azure-mobile-services/commit/6af8b30) [58c5a44](https://github.com/Azure/azure-mobile-services/commit/58c5a44) - Added partial fix for issue [#615](https://github.com/WindowsAzure/azure-mobile-services/issues/615), by removing operations from the queue before releasing the operation's lock. [a28ae32](https://github.com/Azure/azure-mobile-services/commit/a28ae32) ### Managed SDK: Version 1.3.1 - Update to latest version of sqlite pcl [ce1aa67](https://github.com/Azure/azure-mobile-services/commit/ce1aa67) - Fix iOS classic compilation issues [316a57a](https://github.com/Azure/azure-mobile-services/commit/316a57a) - Update Xamarin unified support for Xamarin.iOS 8.6 [da537b1](https://github.com/Azure/azure-mobile-services/commit/da537b1) - Xamarin.iOS Unified API Support [d778c60](https://github.com/Azure/azure-mobile-services/commit/d778c60) - Relax queryId restrictions #521 [offline] [3e2f645](https://github.com/Azure/azure-mobile-services/commit/3e2f645) - Work around for resource missing error on windows phone [offline] ### Managed SDK: Version 1.3 - allow underscore and hyphen in queryId [7d192a3](https://github.com/Azure/azure-mobile-services/commit/7d192a3) - added force option to purge data and pending operations on data [aa51d9f](https://github.com/Azure/azure-mobile-services/commit/aa51d9f) - delete errors with operation on cancel and collapse [372ba61](https://github.com/Azure/azure-mobile-services/commit/372ba61) - rename queryKey to queryId [93e59f7](https://github.com/Azure/azure-mobile-services/commit/93e59f7) - insert should throw if the item already exists [#491](https://github.com/Azure/azure-mobile-services/issues/491) [fc13891](https://github.com/Azure/azure-mobile-services/commit/fc13891) - **[Breaking]** Removed PullAsync overloads that do not take queryId [88cac8c](https://github.com/Azure/azure-mobile-services/commit/88cac8c) ### Managed SDK: Version 1.3 beta3 - Improved the push failure error message [d49a72e](https://github.com/Azure/azure-mobile-services/commit/d49a72e) - Implement true upsert [c5b0b38](https://github.com/Azure/azure-mobile-services/commit/c5b0b38) - Use more fine grained types in sqlite store [de49712](https://github.com/Azure/azure-mobile-services/commit/de49712) - Speedup store table creation [eb7cc8d](https://github.com/Azure/azure-mobile-services/commit/eb7cc8d) - Allow query on member name datetime [7d831cd](https://github.com/Azure/azure-mobile-services/commit/7d831cd) - Make the sync handler optional as there is alternate way for handling sync errors [edc04e5](https://github.com/Azure/azure-mobile-services/commit/edc04e5) - Drop the unused createdat column in operations table [8a30df4](https://github.com/Azure/azure-mobile-services/commit/8a30df4) - Remove redundant overloads in interface and move them to extensions [d0a46b6](https://github.com/Azure/azure-mobile-services/commit/d0a46b6) - Support relative and absolute uri in pull same as table.read [c9d8e39](https://github.com/Azure/azure-mobile-services/commit/c9d8e39) - Allow relative URI in invokeapi [5b3c6b3](https://github.com/Azure/azure-mobile-services/commit/5b3c6b3) - Fixed the like implementation in sqlite store [77a0180](https://github.com/Azure/azure-mobile-services/commit/77a0180) - Purge should forget the deltatoken [18f1803](https://github.com/Azure/azure-mobile-services/commit/18f1803) - Renamed fromServer to ignoreMissingColumns [8b047eb](https://github.com/Azure/azure-mobile-services/commit/8b047eb) - **[Breaking]** Removed PullAsync overloads that do not take queryKey [d4ff784](https://github.com/Azure/azure-mobile-services/commit/d4ff784) - Save tableKind in the errors table [23f2ef0](https://github.com/Azure/azure-mobile-services/commit/23f2ef0) ### Managed SDK: Version 1.3 beta2 - Updated Nuget references - Request __deleted system property for sync - Default delta token set to 1970-01-01 for compatibility with Table Storage - Expose protected methods from the MobileServiceSQLiteStore for intercepting sql - **[Breaking]** Expose a ReadOnlyCollection instead of IEnumerable from MobileServiceTableOperationError ### Managed SDK: Version 1.3 beta - Added support for incremental sync for .NET backend - Added support for byte[] properties in offline - Fixed issue with timezone roundtripping in incremental sync - Improved exception handling for 409 conflicts - Improved error handling for timeout errors during sync - Follow link headers returned from .NET backend and use skip and top for PullAsync() - Introduced the SupportedOptions enum on IMobileServiceSyncTable to configure the pull strategy - **[Breaking]** Do not Push changes on PurgeAsync() instead throw an exception - **[Breaking]** Renamed ToQueryString method to ToODataString on MobileServiceTableQueryDescription class ### Managed SDK: Version 1.3 alpha2 - Added support for incremental sync (currently, for Mobile Services JavaScript backend only) - Added client support for soft delete - Added support for offline pull with query string ### Managed SDK: Version 1.3 alpha2 - Added support for offline and sync - Added support for soft delete ### Managed SDK: Version 1.2.6 - Fixed an issue on Xamarin.iOS and Xamarin.Android where UI popups occur during failed user authentication flows. These popups are now suppressed so that the developer can handle the error however they want. ### Managed SDK: Version 1.2.5 - Updated to use a modified build of Xamarin.Auth that will not conflict with any user-included version of Xamarin.Auth ### Managed SDK: Version 1.2.4 - Added support for following link headers returned from the .NET backend - Added a MobileServiceConflictException to detect duplicate inserts - Added support for datetimeoffsets in queries - Added support for sending provider specific query string parameters in LoginAsync() - Fixed an issue causing duplicate registrations in Xamarin.iOS against .NET backends ### Managed SDK: Version 1.2.3 - Added support for Xamarin iOS Azure Notification Hub integration ### Managed SDK: Version 1.2.2 - Support for optimistic concurrency on delete - Update to Push surface area with minor object model changes. Added Registration base class in PCL and changed name within each extension to match the push notifcation surface. Example: WnsRegistration, WnsTemplateRegistration - Added support for Xamarin Android Azure Notification Hub integration ### Managed SDK: Version 1.2.1 - Added support for Windows Phone 8.1, requires using Visual Studio 2013 Update 2 RC ### Managed SDK: Version 1.1.5 - Added support for Xamarin (iOS / Android) - Clean-up id validation on insert operations ### Managed SDK: Version 1.1.4 - Added support for Windows Azure Notification Hub integration. ### Managed SDK: Version 1.1.3 - Added support for the Windows Azure Active Directory authentication in the `MobileServiceAuthenticationProvider` enumeration. - Also added a mapping from that name to the value used in the service REST API (`/login/aad`) - Fixed a issue [#213](https://github.com/WindowsAzure/azure-mobile-services/issues/213) in which SDK prevented calls to custom APIs with query string parameters starting with `$` ### Managed SDK: Version 1.1.2 - Fix [#192](https://github.com/WindowsAzure/azure-mobile-services/issues/192) - Serialized query is ambiguous if double literal has no fractional part - Fixed Nuget support for Windows Phone 8 ### Managed SDK: Version 1.1.1 - Fix bug when inserting a derived type - Dropped support for Windows Phone 7.x clients (WP7.5 can still use the client version 1.1.0) ### Managed SDK: Version 1.1.0 - Support for tables with string ids - Support for optimistic concurrency (version / ETag) validation - Support for `__createdAt` / `__updatedAt` table columns - Overload for log in which takes the provider as a string, in addition to the one with enums - Fix [#121](https://github.com/WindowsAzure/azure-mobile-services/issues/121) - exceptions in `MobileServiceIncrementalLoadingCollection.LoadMoreItemsAsync` causes the app to crash ### Managed SDK: Version 1.0.3: - Fixed query issues in Visual Basic expressions ================================================ FILE: CHANGELOG.md ================================================ # Azure Mobile Services Change Log ## [iOS SDK](CHANGELOG.ios.md) ## [Managed SDK](CHANGELOG.managed.md) ## [Android SDK](CHANGELOG.android.md) ## [JavaScript SDK](CHANGELOG.javascript.md) ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ================================================ FILE: README.md ================================================ # Microsoft Azure Mobile Services Azure Mobile Services has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [Mobile Apps in Azure App Service](https://docs.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-value-prop). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Download Source Code To get the source code of our SDKs and samples via **git** just type: git clone https://github.com/Azure/azure-mobile-services.git cd ./azure-mobile-services/ ## Reference Documentation #### Azure App Service Mobile Apps * [iOS](http://azure.github.io/azure-mobile-services/iOS/v3) #### Mobile Services * [iOS](http://azure.github.io/azure-mobile-services/iOS/v2) * [Android](http://dl.windowsazure.com/androiddocs/) ## Change log - [iOS SDK](CHANGELOG.ios.md) - [Managed SDK](CHANGELOG.managed.md) - [Android SDK](CHANGELOG.android.md) - [JavaScript SDK](CHANGELOG.javascript.md) ## Managed Windows Client SDK Our managed portable library for Windows 8, Windows Phone 8, Windows Phone 8.1, and Windows Runtime Universal C# Client SDK makes it incredibly easy to use Mobile Services from your Windows applications. The [Microsoft Azure Mobile Services SDK](http://nuget.org/packages/WindowsAzure.MobileServices/) is available as a Nuget package or you can download the source using the instructions above. The managed portable library also supports the full .NET 4.5 platform. ### Prerequisites The SDK requires Visual Studio 2013. ### Building and Referencing the SDK The managed portable library solution includes a core portable assembly and platform-specific assemblies for each of the supported platforms: Windows 8, Windows Phone 8 and .NET 4.5. The core portable platform project is ```Microsoft.WindowsAzure.Mobile```. The platform-specific assembly projects are named using a ```Microsoft.WindowsAzure.Mobile.Ext.``` convention. The Windows Phone 8 platform also include a ```Microsoft.WindowsAzure.Mobile.UI.``` project that contain UI components. To build the Managed Portable Libray: 1. Open the ```sdk\Managed\Microsoft.WindowsAzure.Mobile.Managed.sln``` solution file in Visual Studio 2012. 2. Press F6 to build the solution. ### Running the Tests The managed portable library ```Microsoft.WindowsAzure.Mobile.Managed.sln``` has a test application for each of the supported platforms: Windows 8, Windows Phone 8 and .NET 4.5. 1. Open the ```sdk\Managed\Microsoft.WindowsAzure.Mobile.Managed.sln``` solution file in Visual Studio 2012. 2. Right-click on the test project for a given platform in the Solution Explorer and select ```Set as StartUp Project```. 3. Press F5 to run the application in debug mode. 4. An application will appear with a prompt for a runtime Uri and Tags. You can safely ignore this prompt and just click the Start button. 5. The test suite will run and display the results. ## iOS Client SDK Add a cloud backend to your iOS application in minutes with our iOS client SDK. You can [download the iOS SDK](https://go.microsoft.com/fwLink/?LinkID=266533&clcid=0x409) directly or you can download the source code using the instructions above. ### Prerequisites The SDK requires Xcode 4.6.3 or greater. ### Building and Referencing the SDK 1. Open the ```sdk\iOS\WindowsAzureMobileServices.xcodeproj``` file in Xcode. 2. Set the active scheme option to ```Framework\iOS Device```. 3. Build the project using Command-B. The ```WindowsAzureMobileServices.framework``` folder should be found in the build output folder under ```Products\-iphoneos```. 4. Drag and drop the ```WindowsAzureMobileServices.framework``` from a Finder window into the Frameworks folder of the Project Navigator panel of your iOS application Xcode project. ### Running the Tests 1. Open the ```sdk\iOS\WindowsAzureMobileServices.xcodeproj``` file in Xcode. 2. Set the active scheme option to ```WindowsAzureMobileServices\* Simulator```. 3. Open the ```Test\WindowsAzureMobileServicesFunctionalTests.m``` file in the Project Navigator panel of Xcode. 4. In the ```settings.plist``` file, set ```TestAppUrl``` and ```TestAppApplicationKey``` to a valid URL and Application Key for a working Mobile Service. 5. Run the tests using Command-U. ## Android SDK Microsoft Azure Mobile Services can be used with an Android-based device using our Android SDK. You can get the Android SDK in one of the following two ways or you can download the source code using the instructions above. 1. For an Android studio project, include the line `compile 'com.microsoft.azure:azure-mobile-services-android-sdk:2.0.3'` in the dependencies section of build.gradle file of the app 2. Eclipse users can [download the Android SDK](https://go.microsoft.com/fwLink/?LinkID=280126&clcid=0x409) directly or can download the source code using the instructions above. ### Prerequisites The SDK requires Android Studio. ### Building and Referencing the SDK 1. Open the folder `\azure-mobile-services\sdk\android` using the option `Open an existing Android Studio Project` in Android Studio. 2. Project should be built automatically, In case it does not build, Right click on `sdk` folder and select `Make Module 'sdk'` 3. The file `sdk-release.aar` should be present at `\azure-mobile-services\sdk\android\src\sdk\build\outputs\aar` 4. Rename the file `sdk-release.aar` to `sdk-release.zip` 5. Extract the zip file, `classes.jar` should be present in the root folder. ### Running the Tests The SDK has a suite of unit tests that you can easily run. 1. Open the folder `\azure-mobile-services\sdk\android` using the option `Open an existing Android Studio Project` in Android Studio. 2. Project should be built automatically, In case it does not build, Right click on `sdk` folder and select `Make Module 'sdk.testapp'` 3. Expand `sdk.testapp` and sub folder `java` 4. Right click on `com.microsoft.windowsazure.mobileservices.sdk.testapp`, Select `Run`, Select `Tests in com.microsoft.windowsazure.mobileservices.sdk.testapp` (with Android tests icon) ## JavaScript SDK Our JavaScript SDK makes it easy to use our Microsoft Azure Mobile Services in a Windows 8 application or an HTML client. The [Microsoft Azure Mobile Services for WinJS SDK](http://nuget.org/packages/WindowsAzure.MobileServices.WinJS/) is available as a Nuget package or you can download the source for both WinJS and HTML using the instructions above. ### Prerequisites The Microsoft Azure Mobile Services for WinJS SDK requires Windows 8.1 and Visual Studio 2013 Update 3. ### Building and Referencing the SDK 1. Install Node.js and grunt-cli (globally) for building in Visual Studio 2. Install the Task Runner Explorer(https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708) add on for VS 2013 3. Open the ```sdk\JavaScript\Microsoft.WindowsAzure.Mobile.JS.sln``` file in Visual Studio. 4. Right click on the gruntfile.js in the solution, and select Task Runner Explorer 5. Run the default build option Alternatively, you can use Grunt from the command line to build the project as well. For WinJS Windows Store apps, copy the ```Generated/MobileServices[.min].js```, ```Generated/MobileServices.DevIntellisense.js``` and ```Generated/MobileService.pri``` files into your WinJS project. For HTML applications, copy the ```Generated/MobileServices.Web[.min].js``` and the ```Generated/MobileServices.DevIntellisense.js``` files into your HTML\JavaScript project. ### Running the Tests To run the WinJS Windows Store test app: 1. Open the ```sdk\JavaScript\Microsoft.WindowsAzure.Mobile.JS.sln``` file in Visual Studio. 2. In the Solution Explorer, right-click on the ```Microsoft.WindowsAzure.Mobile.WinJS.Test``` project in the Solution Explorer and select ```Set as StartUp Project```. 3. Press F5 to run the application in debug mode. 4. A Windows Store application will appear with a prompt for a Runtime Uri and Tags. You can safely ignore this prompt and just click the Start button. 5. The test suite will run and display the results. To run the HTML tests: 1. Open the ```sdk\JavaScript\Microsoft.WindowsAzure.Mobile.JS.sln``` file in Visual Studio. 2. In the Solution Explorer, select the Microsoft.WindowsAzure.Mobile.WinJS.Test project and right-click to select 'View in Browser'. 3. The default browser will launch and run the test HTML application. Some tests may fail because due to an 'Unexpected connection failure'. This is because the test is configured to connect to a Mobile Service that does not exist. These failures can be ignored. ## Useful Resources * [Quickstarts](https://github.com/Azure/azure-mobile-services-quickstarts) * [E2E Test Suite](https://github.com/Azure/azure-mobile-services-test) * [Samples](https://github.com/Azure/mobile-services-samples) * Tutorials and product overview are available at [Microsoft Azure Mobile Services Developer Center](http://azure.microsoft.com/en-us/develop/mobile). * Our product team actively monitors the [Mobile Services Developer Forum](http://social.msdn.microsoft.com/Forums/en-US/azuremobile/) to assist you with any troubles. ## Contribute Code or Provide Feedback This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. If you would like to become an active contributor to this project please follow the instructions provided in [Microsoft Azure Projects Contribution Guidelines](http://azure.github.com/guidelines.html). If you encounter any bugs with the library please file an issue in the [Issues](https://github.com/Azure/azure-mobile-services/issues) section of the project. ================================================ FILE: component/.gitignore ================================================ *.xam xpkg *.suo bin obj lib ================================================ FILE: component/Details.md ================================================ Add structured storage, authentication, push notifications and more to your Xamarin based mobile application in minutes using Microsoft Azure Mobile Services. To get started, follow the tutorial for the appropriate platform: - [Xamarin.iOS](https://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-xamarin-ios/) - [Xamarin.Android](https://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-xamarin-android/) To learn about more Mobile Services, visit the [Microsoft Azure Mobile Developer Center](https://www.windowsazure.com/en-us/develop/mobile/). ================================================ FILE: component/GettingStarted.md ================================================ ## Getting Started Follow the tutorial for the appropriate platform: - [Xamarin.iOS](https://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-xamarin-ios/) - [Xamarin.Android](https://www.windowsazure.com/en-us/develop/mobile/tutorials/get-started-xamarin-android/) ### Source Code - GitHub: https://github.com/WindowsAzure/azure-mobile-services ### Documentation - Tutorials: https://www.windowsazure.com/en-us/develop/mobile/resources/ - Developer Center: http://www.windowsazure.com/mobile - API Library: http://msdn.microsoft.com/en-us/library/windowsazure/jj710108.aspx - Xamarin Mobile Services client framework GitHub Repo: https://github.com/xamarin/azure-mobile-services ### Contact - Developer Forum: http://social.msdn.microsoft.com/Forums/en-US/azuremobile/threads - Feature Requests: http://mobileservices.uservoice.com - Contact: mobileservices@microsoft.com - Twitter: @paulbatum @joshtwist @cloudnick @chrisrisner @mlunes90 ### Legal - Terms & Conditions: http://www.windowsazure.com/en-us/support/legal/ ================================================ FILE: component/License.md ================================================ # Apache License Version 2.0, January 2004
[http://www.apache.org/licenses/](http://www.apache.org/licenses/) TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ================================================ FILE: component/component.yaml ================================================ id: azure-mobile-services version: 1.2.1 name: Azure Mobile Services publisher: Microsoft publisher-url: http://www.windowsazure.com/en-us/develop/mobile/ summary: Store data in the cloud, authenticate users, and send push notifications. license: LICENSE.md details: Details.md getting-started: GettingStarted.md icons: - azure-mobile-services_512x512.png - azure-mobile-services_128x128.png libraries: ios: - ..\..\bin\Signed\Release\Managed\iOS-Classic\Microsoft.WindowsAzure.Mobile.dll - ..\..\bin\Signed\Release\Managed\iOS-Classic\Microsoft.WindowsAzure.Mobile.Ext.dll - ..\..\bin\Signed\Release\Managed\iOS-Classic\Newtonsoft.Json.dll - ..\..\bin\Signed\Release\Managed\iOS-Classic\System.Net.Http.Extensions.dll - ..\..\bin\Signed\Release\Managed\iOS-Classic\System.Net.Http.Primitives.dll ios-unified: - ..\..\bin\Signed\Release\Managed\iOS\Microsoft.WindowsAzure.Mobile.dll - ..\..\bin\Signed\Release\Managed\iOS\Microsoft.WindowsAzure.Mobile.Ext.dll - ..\..\bin\Signed\Release\Managed\iOS\Newtonsoft.Json.dll - ..\..\bin\Signed\Release\Managed\iOS\System.Net.Http.Extensions.dll - ..\..\bin\Signed\Release\Managed\iOS\System.Net.Http.Primitives.dll android: - ..\..\bin\Signed\Release\Managed\Android\Microsoft.WindowsAzure.Mobile.dll - ..\..\bin\Signed\Release\Managed\Android\Microsoft.WindowsAzure.Mobile.Ext.dll - ..\..\bin\Signed\Release\Managed\Android\Newtonsoft.Json.dll - ..\..\bin\Signed\Release\Managed\Android\System.Net.Http.Extensions.dll - ..\..\bin\Signed\Release\Managed\Android\System.Net.Http.Primitives.dll mobile: - ..\..\bin\Signed\Release\Managed\Portable\Microsoft.WindowsAzure.Mobile.dll - ..\..\bin\Signed\Release\Managed\Portable\Newtonsoft.Json.dll samples: - name: Android Sample path: samples\androidsample\androidsample.sln - name: iOS Unified API Sample path: samples\iOSsample\iOSsample.sln screenshots: - screenshots/WAMS-New.PNG - screenshots/WAMS-SQLdb1.png - screenshots/WAMS-SQLdb2.png - screenshots/WAMS-userauth.png - screenshots/WAMS-push1.png - screenshots/WAMS-push2.png - screenshots/WAMS-Create.png - screenshots/WAMS-Script1.png - screenshots/WAMS-Script2.png - screenshots/WAMS-Scheduler2.png - screenshots/WAMS-Keys.png - screenshots/WAMS-EmptyData.png - screenshots/WAMS-DataPermissions.png ================================================ FILE: component/samples/androidsample/androidsample/Assets/AboutAssets.txt ================================================ Any raw assets you want to be deployed with your application can be placed in this directory (and child directories) and given a Build Action of "AndroidAsset". These files will be deployed with your package and will be accessible using Android's AssetManager, like this: public class ReadAsset : Activity { protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); InputStream input = Assets.Open ("my_asset.txt"); } } Additionally, some Android functions will automatically load asset files: Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf"); ================================================ FILE: component/samples/androidsample/androidsample/Properties/AndroidManifest.xml ================================================  ================================================ FILE: component/samples/androidsample/androidsample/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using Android.App; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. [assembly: AssemblyTitle("androidsample")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("")] [assembly: AssemblyCopyright("billholmes")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. [assembly: AssemblyVersion("1.0.0")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. //[assembly: AssemblyDelaySign(false)] //[assembly: AssemblyKeyFile("")] ================================================ FILE: component/samples/androidsample/androidsample/Resources/AboutResources.txt ================================================ Images, layout descriptions, binary blobs and string dictionaries can be included in your application as resource files. Various Android APIs are designed to operate on the resource IDs instead of dealing with images, strings or binary blobs directly. For example, a sample Android app that contains a user interface layout (main.axml), an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png) would keep its resources in the "Resources" directory of the application: Resources/ drawable/ icon.png layout/ main.axml values/ strings.xml In order to get the build system to recognize Android resources, set the build action to "AndroidResource". The native Android APIs do not operate directly with filenames, but instead operate on resource IDs. When you compile an Android application that uses resources, the build system will package the resources for distribution and generate a class called "R" (this is an Android convention) that contains the tokens for each one of the resources included. For example, for the above Resources layout, this is what the R class would expose: public class R { public class drawable { public const int icon = 0x123; } public class layout { public const int main = 0x456; } public class strings { public const int first_string = 0xabc; public const int second_string = 0xbcd; } } You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main to reference the layout/main.axml file, or R.strings.first_string to reference the first string in the dictionary file values/strings.xml. ================================================ FILE: component/samples/androidsample/androidsample/Resources/Resource.designer.cs ================================================ #pragma warning disable 1591 //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.34011 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ [assembly: global::Android.Runtime.ResourceDesignerAttribute("androidsample.Resource", IsApplication=true)] namespace androidsample { [System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")] public partial class Resource { static Resource() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } public static void UpdateIdValues() { } public partial class Attribute { static Attribute() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Attribute() { } } public partial class Drawable { // aapt resource value: 0x7f020000 public const int ic_launcher = 2130837504; static Drawable() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Drawable() { } } public partial class Id { // aapt resource value: 0x7f070000 public const int LinearLayout1 = 2131165184; // aapt resource value: 0x7f070004 public const int buttonAddToDo = 2131165188; // aapt resource value: 0x7f070006 public const int checkToDoItem = 2131165190; // aapt resource value: 0x7f070005 public const int listViewToDo = 2131165189; // aapt resource value: 0x7f070002 public const int loadingProgressBar = 2131165186; // aapt resource value: 0x7f070007 public const int menu_refresh = 2131165191; // aapt resource value: 0x7f070003 public const int textNewToDo = 2131165187; // aapt resource value: 0x7f070001 public const int textViewTitle = 2131165185; static Id() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Id() { } } public partial class Layout { // aapt resource value: 0x7f030000 public const int Activity_To_Do = 2130903040; // aapt resource value: 0x7f030001 public const int Row_List_To_Do = 2130903041; static Layout() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Layout() { } } public partial class Menu { // aapt resource value: 0x7f060000 public const int activity_main = 2131099648; static Menu() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Menu() { } } public partial class String { // aapt resource value: 0x7f040002 public const int add_button_text = 2130968578; // aapt resource value: 0x7f040001 public const int add_textbox_hint = 2130968577; // aapt resource value: 0x7f040000 public const int app_name = 2130968576; // aapt resource value: 0x7f040005 public const int checkbox_text = 2130968581; // aapt resource value: 0x7f040004 public const int menu_refresh = 2130968580; // aapt resource value: 0x7f040003 public const int mobile_services = 2130968579; static String() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private String() { } } public partial class Style { // aapt resource value: 0x7f050000 public const int AppBaseTheme = 2131034112; // aapt resource value: 0x7f050001 public const int AppTheme = 2131034113; static Style() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); } private Style() { } } } } #pragma warning restore 1591 ================================================ FILE: component/samples/androidsample/androidsample/Resources/layout/Activity_To_Do.axml ================================================ ================================================ FILE: component/samples/iOSsample/iOSsample/MainStoryboard_iPhone.storyboard ================================================ ================================================ FILE: component/samples/iOSsample/iOSsample/QSTodoListViewController.cs ================================================ // This file has been autogenerated from parsing an Objective-C header file added in Xcode. using System; using UIKit; using Foundation; using System.Threading.Tasks; namespace iOSsample { public partial class QSTodoListViewController : UITableViewController { QSTodoService todoService; bool useRefreshControl = false; public QSTodoListViewController (IntPtr handle) : base (handle) { } public override async void ViewDidLoad () { base.ViewDidLoad (); try { todoService = new QSTodoService(); } catch (UriFormatException) { var alert = new UIAlertView("Error", "Please make sure you update the applicationURL and applicationKey to match the mobile service you have created.", null, "OK"); alert.Show(); return; } todoService.BusyUpdate += (bool busy) => { if (busy) activityIndicator.StartAnimating (); else activityIndicator.StopAnimating (); }; await RefreshAsync (); AddRefreshControl (); } async Task RefreshAsync () { // only activate the refresh control if the feature is available if (useRefreshControl) RefreshControl.BeginRefreshing (); await todoService.RefreshDataAsync (); if (useRefreshControl) RefreshControl.EndRefreshing (); TableView.ReloadData (); } #region UITableView methods public override nint RowsInSection (UITableView tableview, nint section) { if (todoService == null || todoService.Items == null) return 0; return todoService.Items.Count; } public override nint NumberOfSections (UITableView tableView) { return 1; } public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath) { const string CellIdentifier = @"Cell"; var cell = tableView.DequeueReusableCell (CellIdentifier); if (cell == null) { cell = new UITableViewCell (UITableViewCellStyle.Default, CellIdentifier); } // Set the label on the cell and make sure the label color is black (in case this cell // has been reused and was previously greyed out var label = (UILabel)cell.ViewWithTag ((nint)1); label.TextColor = UIColor.Black; label.Text = todoService.Items [indexPath.Row].Text; return cell; } public override string TitleForDeleteConfirmation (UITableView tableView, NSIndexPath indexPath) { // Customize the Delete button to say "complete" return @"complete"; } public override UITableViewCellEditingStyle EditingStyleForRow (UITableView tableView, NSIndexPath indexPath) { // Find the item that is about to be edited var item = todoService.Items [indexPath.Row]; // If the item is complete, then this is just pending upload. Editing is not allowed if (item.Complete) return UITableViewCellEditingStyle.None; // Otherwise, allow the delete button to appear return UITableViewCellEditingStyle.Delete; } async public override void CommitEditingStyle (UITableView tableView, UITableViewCellEditingStyle editingStyle, NSIndexPath indexPath) { // Find item that was commited for editing (completed) var item = todoService.Items [indexPath.Row]; // Change the appearance to look greyed out until we remove the item var label = (UILabel)TableView.CellAt (indexPath).ViewWithTag (1); label.TextColor = UIColor.Gray; // Ask the todoService to set the item's complete value to YES, and remove the row if successful await todoService.CompleteItemAsync (item); // Remove the row from the UITableView tableView.DeleteRows (new [] { indexPath }, UITableViewRowAnimation.Top); } #endregion #region UI Actions async partial void OnAdd (NSObject sender) { if (string.IsNullOrWhiteSpace (itemText.Text)) return; var newItem = new ToDoItem { Text = itemText.Text, Complete = false }; await todoService.InsertTodoItemAsync (newItem); var index = todoService.Items.FindIndex (item => item.Id == newItem.Id); TableView.InsertRows (new [] { NSIndexPath.FromItemSection (index, 0) }, UITableViewRowAnimation.Top); itemText.Text = ""; } #endregion #region UITextFieldDelegate methods [Foundation.Export("textFieldShouldReturn:")] public virtual bool ShouldReturn (UITextField textField) { textField.ResignFirstResponder (); return true; } #endregion #region * iOS Specific Code // This method will add the UIRefreshControl to the table view if // it is available, ie, we are running on iOS 6+ void AddRefreshControl () { if (UIDevice.CurrentDevice.CheckSystemVersion (6, 0)) { // the refresh control is available, let's add it RefreshControl = new UIRefreshControl (); RefreshControl.ValueChanged += async (sender, e) => { await RefreshAsync (); }; useRefreshControl = true; } } #endregion } } ================================================ FILE: component/samples/iOSsample/iOSsample/QSTodoListViewController.designer.cs ================================================ // WARNING // // This file has been generated automatically by Xamarin Studio to store outlets and // actions made in the Xcode designer. If it is removed, they will be lost. // Manual changes to this file may not be handled correctly. // using Foundation; namespace iOSsample { [Foundation.Register("QSTodoListViewController")] partial class QSTodoListViewController { [Foundation.Outlet] UIKit.UIActivityIndicatorView activityIndicator { get; set; } [Foundation.Outlet] UIKit.UITextField itemText { get; set; } [Foundation.Action("OnAdd:")] partial void OnAdd (Foundation.NSObject sender); void ReleaseDesignerOutlets () { if (itemText != null) { itemText.Dispose (); itemText = null; } if (activityIndicator != null) { activityIndicator.Dispose (); activityIndicator = null; } } } } ================================================ FILE: component/samples/iOSsample/iOSsample/QSTodoService.cs ================================================ using System; using System.Net.Http; using System.Threading.Tasks; using System.Collections.Generic; using Microsoft.WindowsAzure.MobileServices; namespace iOSsample { public class QSTodoService : DelegatingHandler { const string applicationURL = @"MOBILE SERVICE URL"; const string applicationKey = @"APPLICATION KEY"; MobileServiceClient client; IMobileServiceTable todoTable; int busyCount = 0; public event Action BusyUpdate; public QSTodoService () { CurrentPlatform.Init (); // Initialize the Mobile Service client with your URL and key client = new MobileServiceClient (applicationURL, applicationKey, this); // Create an MSTable instance to allow us to work with the TodoItem table todoTable = client.GetTable (); } public List Items { get; private set;} async public Task> RefreshDataAsync () { try { // This code refreshes the entries in the list view by querying the TodoItems table. // The query excludes completed TodoItems Items = await todoTable .Where (todoItem => todoItem.Complete == false).ToListAsync (); } catch (MobileServiceInvalidOperationException e) { Console.Error.WriteLine (@"ERROR {0}", e.Message); return null; } return Items; } public async Task InsertTodoItemAsync (ToDoItem todoItem) { try { // This code inserts a new TodoItem into the database. When the operation completes // and Mobile Services has assigned an Id, the item is added to the CollectionView await todoTable.InsertAsync (todoItem); Items.Add (todoItem); } catch (MobileServiceInvalidOperationException e) { Console.Error.WriteLine (@"ERROR {0}", e.Message); } } public async Task CompleteItemAsync (ToDoItem item) { try { // This code takes a freshly completed TodoItem and updates the database. When the MobileService // responds, the item is removed from the list item.Complete = true; await todoTable.UpdateAsync (item); Items.Remove (item); } catch (MobileServiceInvalidOperationException e) { Console.Error.WriteLine (@"ERROR {0}", e.Message); } } void Busy (bool busy) { // assumes always executes on UI thread if (busy) { if (busyCount++ == 0 && BusyUpdate != null) BusyUpdate (true); } else { if (--busyCount == 0 && BusyUpdate != null) BusyUpdate (false); } } #region implemented abstract members of HttpMessageHandler protected override async Task SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) { Busy (true); var response = await base.SendAsync (request, cancellationToken); Busy (false); return response; } #endregion } } ================================================ FILE: component/samples/iOSsample/iOSsample/ToDoItem.cs ================================================ using System; using Newtonsoft.Json; namespace iOSsample { public class ToDoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } } } ================================================ FILE: component/samples/iOSsample/iOSsample/app.config ================================================  ================================================ FILE: component/samples/iOSsample/iOSsample/iOSsample.csproj ================================================  Debug iPhoneSimulator 10.0.0 2.0 {07F1CD1B-626F-4BE5-9840-97B44CBB94C9} {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} Exe iOSsample Resources iOSsample true full false bin\iPhoneSimulator\Debug DEBUG; prompt 4 SdkOnly false True 8.1 False False True Default, i386 False False False False False full true bin\iPhoneSimulator\Release prompt 4 None false true full false bin\iPhone\Debug DEBUG; prompt 4 true false iPhone Developer full true bin\iPhone\Release prompt 4 iPhone Developer false full true bin\iPhone\Ad-Hoc prompt 4 iPhone Developer true false full true bin\iPhone\AppStore prompt 4 iPhone Distribution false Automatic:AppStore False ..\..\..\lib\ios-unified\Microsoft.WindowsAzure.Mobile.dll False ..\..\..\lib\ios-unified\Microsoft.WindowsAzure.Mobile.Ext.dll False ..\..\..\lib\ios-unified\Newtonsoft.Json.dll False ..\..\..\lib\ios-unified\System.Net.Http.Extensions.dll False ..\..\..\lib\ios-unified\System.Net.Http.Primitives.dll QSTodoListViewController.cs ================================================ FILE: component/samples/iOSsample/iOSsample.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30110.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iOSsample", "iOSsample\iOSsample.csproj", "{07F1CD1B-626F-4BE5-9840-97B44CBB94C9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|iPhone = Debug|iPhone Debug|iPhoneSimulator = Debug|iPhoneSimulator Release|iPhone = Release|iPhone Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhone.ActiveCfg = Debug|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhone.Build.0 = Debug|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhone.Deploy.0 = Debug|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Debug|iPhoneSimulator.Deploy.0 = Debug|iPhoneSimulator {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhone.ActiveCfg = Release|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhone.Build.0 = Release|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhone.Deploy.0 = Release|iPhone {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {07F1CD1B-626F-4BE5-9840-97B44CBB94C9}.Release|iPhoneSimulator.Deploy.0 = Release|iPhoneSimulator EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = iOSsample\iOSsample.csproj EndGlobalSection EndGlobal ================================================ FILE: docs/README.md ================================================ # Azure Mobile Services Documentation Azure Mobile Services has been deprecated. This documentation is a copy of the documentation at the point at which the service was deprecated. No further updates will be made to these documents. This service has been superseded by Azure App Service Mobile Apps. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). List of documents (from the original left-hand navigation) Additional documents can be accessed by browsing the directory. * Get started * [Get started with iOS](mobile-services-ios-get-started.md) * [Get started with Xamarin Android](partner-xamarin-mobile-services-android-get-started.md) * [Get started with Xamarin iOS](partner-xamarin-mobile-services-ios-get-started.md) * [Get started with Android](mobile-services-android-get-started.md) * [Get started with Windows](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) * Store data * [Connect your app to Table storage](mobile-services-dotnet-backend-store-data-table-storage.md) * [Connect your app to DocumentDB](http://giventocode.com/how-to-use-azure-documentdb-with-azure-mobile-services#.VOZ7csJ0x9C) * [Connect your app to an on-premise SQL Database](mobile-services-dotnet-backend-hybrid-connections-get-started.md) * [Connect your app to an existing SQL Database](mobile-services-dotnet-backend-use-existing-sql-database.md) * Enable offline & Sync * [Sync your app data and work offline](mobile-services-ios-get-started-offline-data.md) * [Handle conflicts with offline data Sync](mobile-services-ios-handling-conflicts-offline-data.md) * Authentication users * [Add Active Directory authentication to your app](mobile-services-dotnet-backend-ios-adal-sso-authentication.md) * [Add Facebook, Google or Twitter authentication to your app](mobile-services-ios-get-started-users.md) * [Add custom authentication to your app](mobile-services-dotnet-backend-get-started-custom-authentication.md) * Send push notifications * [Add push notifications to your app](mobile-services-javascript-backend-ios-get-started-push.md) * [Send push notifications to authenticated users](mobile-services-javascript-backend-ios-push-notifications-app-users.md) * [Send personalized and localized push notifications](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-ios-xplat-localized-apns-push-notification/) * [Troubleshoot sending push](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-fixer/) * How to manage * [Define a custom endpoint](mobile-services-javascript-backend-define-custom-api.md) * [Use multiple clients with a single mobile service](mobile-services-how-to-use-multiple-clients-single-service.md) * [Build scalable apps that talk to Azure SQL database](mobile-services-sql-scale-guidance.md) * [Prepare for disaster recovery](mobile-services-disaster-recovery.md) * Automate * [Deploy and manage a mobile service using command-line Tools](mobile-services-manage-command-line-interface.md) * [Deploy a mobile service using source control](mobile-services-store-scripts-source-control.md) * [Execute backend tasks on a schedule](mobile-services-schedule-recurring-tasks.md) ================================================ FILE: docs/mobile-services-android-get-started-data.md ================================================ # Add Mobile Services to an existing Android app (JavaScript backend) >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Summary This topic shows you how to use Azure Mobile Services to add persistent data to an Android app. In this tutorial, you will download an app that stores data in memory, create a new mobile service, integrate the app with the mobile service so that it stores and updates data in Azure Mobile Services instead of locally, and then use the Azure classic portal to view changes to data that were made by running the app. This tutorial helps you understand in more detail how Azure Mobile Services can store and retrieve data from an Android app. So it walks you through many of the steps that are already completed for you in the Mobile Services quickstart tutorial. If this is your first experience with Mobile Services, consider first completing the tutorial [Get started with Mobile Services](mobile-services-android-get-started.md). ## Prerequisites To complete this tutorial, you need the following: - an Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see [Azure Free Trial](http://azure.microsoft.com/pricing/free-trial/?WT.mc_id=AED8DE357). - the [Azure Mobile Services Android SDK]; - the [Android Studio integrated development environment](https://developer.android.com/sdk/index.html), which includes the Android SDK; and Android 4.2 or a later version. The downloaded GetStartedWithData project requires Android 4.2 or a later version. However, the Mobile Services SDK requires only Android 2.2 or a later version. ## Sample Code To see the completed source code, go [here](https://github.com/Azure/mobile-services-samples/tree/master/GettingStartedWithData/AndroidStudio). ## Download the GetStartedWithData project ### Get the sample code This tutorial is built on **GetStartedWithData**, which is an Android app. The UI for this app is identical to the one in the Mobile Services Android quickstart, except that items that are added to the list are stored locally in memory. You will add the code needed to persist the data to storage. 1. Download the samples repository from gitHub by clicking here and then Click **Download ZIP**. 2. Unzip the downloaded file and make a note of its location, or move it to your Android Studio projects directory. 3. Open Android Studio. If you are working with a different project and it appears, close the project (**File => Close Project**). 4. Select **Open an existing Android Studio project**, browse to the project location in the *AndroidStudio* folder of *GettingStartedWithData*, and then click **OK.** ![](./media/mobile-services-android-get-started/android-studio-import-project.png) The project is now ready for you to work with. ### Inspect and run the sample code 1. In Project Explorer, expand **app** => **src** => **main** => **java** => **com.example.GetStartedWithData** and then open the *ToDoActivity.java* file. ![](./media/download-android-sample-code/mobile-services-android-studio-project.png) Notice that there are `//TODO` comments that specify the steps you must take to make this app work with your mobile service. 2. From the **Run** menu, click **Run app**. 3. The **Choose Device** dialog will appear. ![](./media/mobile-services-android-run-sample-code/android-studio-choose-device.png) > [AZURE.NOTE] You can run this project using an Android phone, or using the Android emulator. Running with an Android phone requires you to download a phone-specific USB driver. > > To run the project in the Android emulator, you must define a least one Android Virtual Device (AVD). Use the AVD Manager to create and manage these devices. 4. Choose either a connected device, or *Launch Emulator*. 5. When the app appears, type meaningful text, such as _Complete the tutorial_, and then click **Add**. ![](./media/download-android-sample-code/mobile-quickstart-startup-android.png) Notice that the saved text is stored in an in-memory collection and displayed in the list below. ## Create a new mobile service in the Azure classic portal Next, you will create a new mobile service to replace the in-memory list for data storage. Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). 2. At the bottom of the navigation pane, click **+NEW**. ![plus-new](./media/mobile-services-create-new-service-data/plus-new.png) 3. Expand **Compute** and **Mobile Service**, then click **Create**. ![mobile-create](./media/mobile-services-create-new-service-data/mobile-create.png) This displays the **New Mobile Service** dialog. 4. In the **Create a mobile service** page, select **Create a free 20 MB SQL Database**, then type a subdomain name for the new mobile service in the **URL** textbox and wait for name verification. Once name verification completes, click the right arrow button to go to the next page. ![mobile-create-page1](./media/mobile-services-create-new-service-data/mobile-create-page1.png) This displays the **Specify database settings** page. > [AZURE.NOTE] As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 5. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![mobile-create-page2](./media/mobile-services-create-new-service-data/mobile-create-page2.png) > [AZURE.NOTE] When the password that you supply does not meet the minimum requirements or when there is a mismatch, a warning is displayed. > > We recommend that you make a note of the administrator login name and password that you specify; you will need this information to reuse the SQL Database instance or the server in the future. You have now created a new mobile service that can be used by your mobile apps. Next, you will add a new table in which to store app data. This table will be used by the app in place of the in-memory collection. ## Add a new table to the mobile service To be able to store app data in the new mobile service, you must first create a new table in the associated SQL Database instance. 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click the mobile service that you just created. 2. Click the **Data** tab, then click **+Create**. This displays the **Create new table** dialog. 3. In **Table name** type _TodoItem_, then click the check button. This creates a new storage table **TodoItem** with the default permissions set. This means that anyone with the application key, which is distributed with your app, can access and change data in the table. >[AZURE.NOTE] The same table name is used in Mobile Services quickstart. However, each table is created in a schema that is specific to a given mobile service. This is to prevent data collisions when multiple mobile services use the same database. 4. Click the new **TodoItem** table and verify that there are no data rows. 5. Click the **Columns** tab. Verify that the following default columns are automatically created for you:
Column Name Type Index
id string Indexed
__createdAt date Indexed
__updatedAt date -
__version timestamp (MSSQL) -
This is the minimum requirement for a table in Mobile Services. > [AZURE.NOTE] When dynamic schema is enabled on your mobile service, new columns are created automatically when JSON objects are sent to the mobile service by an insert or update operation. You are now ready to use the new mobile service as data storage for the app. ## Update the app to use the mobile service for data access Now that your mobile service is ready, you can update the app to store items in Mobile Services instead of the local collection. 1. Verify that you have the following lines in the **dependencies** tag in the *build.gradle (Module app)* file, and if not add them. This adds references to the Mobile Services Android Client SDK. compile 'com.android.support:support-v4:21.0.3' compile 'com.google.code.gson:gson:2.2.2' compile 'com.google.guava:guava:18.0' compile 'com.microsoft.azure:azure-mobile-services-android-sdk:2.0.2+' 2. Now rebuild the project by clicking on **Sync Project with Gradle Files**. 3. Open the AndroidManifest.xml file and add the following line, which enables the app to access Mobile Services in Azure. 4. In Project Explorer, open the TodoActivity.java file located in the **GetStartedWithData => app => src => java** folder, and uncomment the following lines of code: import java.net.MalformedURLException; import android.os.AsyncTask; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.microsoft.windowsazure.mobileservices.MobileServiceClient; import com.microsoft.windowsazure.mobileservices.MobileServiceList; import com.microsoft.windowsazure.mobileservices.http.NextServiceFilterCallback; import com.microsoft.windowsazure.mobileservices.http.ServiceFilter; import com.microsoft.windowsazure.mobileservices.http.ServiceFilterRequest; import com.microsoft.windowsazure.mobileservices.http.ServiceFilterResponse; import com.microsoft.windowsazure.mobileservices.table.MobileServiceTable; 5. Comment out the following lines: import java.util.ArrayList; import java.util.List; 6. We will remove the in-memory list currently used by the app, so we can replace it with a mobile service. In the **ToDoActivity** class, comment out the following line of code, which defines the existing **toDoItemList** list. public List toDoItemList = new ArrayList(); 7. Save the file, and the project will indicate build errors. Search for the three remaining locations where the `toDoItemList` variable is used and comment out the sections indicated. This fully removes the in-memory list. 8. We now add our mobile service. Uncomment the following lines of code: private MobileServiceClient mClient; private private MobileServiceTable mToDoTable; 9. Find the *ProgressFilter* class at the bottom of the file and uncomment it. This class displays a 'loading' indicator while *MobileServiceClient* is running network operations. 10. In the Azure classic portal, click **Mobile Services**, and then click the mobile service you just created. 11. Click the **Dashboard** tab and make a note of the **Site URL**, then click **Manage keys** and make a note of the **Application key**. ![](./media/download-android-sample-code/mobile-dashboard-tab.png) You will need these values when accessing the mobile service from your app code. 12. In the **onCreate** method, uncomment the following lines of code that define the **MobileServiceClient** variable: try { // Create the Mobile Service Client instance, using the provided // Mobile Service URL and key mClient = new MobileServiceClient( "MobileServiceUrl", "AppKey", this).withFilter(new ProgressFilter()); // Get the Mobile Service Table instance to use mToDoTable = mClient.getTable(ToDoItem.class); } catch (MalformedURLException e) { createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error"); } This creates a new instance of *MobileServiceClient* that is used to access your mobile service. It also creates the *MobileServiceTable* instance that is used to proxy data storage in the mobile service. 13. In the code above, replace `MobileServiceUrl` and `AppKey` with the URL and application key from your mobile service, in that order. 14. Uncommment these lines of the **checkItem** method: new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.update(item).get(); runOnUiThread(new Runnable() { public void run() { if (item.isComplete()) { mAdapter.remove(item); } refreshItemsFromTable(); } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); This sends an item update to the mobile service and removes checked items from the adapter. 15. Uncommment these lines of the **addItem** method: // Insert the new item new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.insert(item).get(); if (!item.isComplete()) { runOnUiThread(new Runnable() { public void run() { mAdapter.add(item); } }); } } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); This code creates a new item and inserts it into the table in the remote mobile service. 16. Uncommment these lines of the **refreshItemsFromTable** method: // Get the items that weren't marked as completed and add them in the adapter new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final MobileServiceList result = mToDoTable.where().field("complete").eq(false).execute().get(); runOnUiThread(new Runnable() { @Override public void run() { mAdapter.clear(); for (ToDoItem item : result) { mAdapter.add(item); } } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); This queries the mobile service and returns all items that are not marked as complete. Items are added to the adapter for binding. ## Test the app against your new mobile service Now that the app has been updated to use Mobile Services for back end storage, you can test it against Mobile Services, using either the Android emulator or an Android phone. 1. From the **Run** menu, click **Run app** to start the project. This executes your app, built with the Android SDK, that uses the client library to send a query that returns items from your mobile service. 5. As before, type meaningful text, then click **Add**. This sends a new item as an insert to the mobile service. 3. In the [Azure classic portal], click **Mobile Services**, and then click your mobile service. 4. Click the **Data** tab, then click **Browse**. ![][9] Notice that the **TodoItem** table now contains data, with some values generated by Mobile Services, and that columns have been automatically added to the table to match the TodoItem class in the app. This concludes the **Get started with data** tutorial for Android. ## Troubleshooting ### Verify Android SDK Version Because of ongoing development, the Android SDK version installed in Android Studio might not match the version in the code. The Android SDK referenced in this tutorial is version 21, the latest at the time of writing. The version number may increase as new releases of the SDK appear, and we recomend using the latest version available. Two symptoms of version mismatch are: 1. When you Build or Rebuild the project, you may get Gradle error messages like "**failed to find target Google Inc.:Google APIs:n**". 2. Standard Android objects in code that should resolve based on `import` statements may be generating error messages. If either of these appear, the version of the Android SDK installed in Android Studio might not match the SDK target of the downloaded project. To verify the version, make the following changes: 1. In Android Studio, click **Tools** => **Android** => **SDK Manager**. If you have not installed the latest version of the SDK Platform, then click to install it. Make a note of the version number. 2. In the Project Explorer tab, under **Gradle Scripts**, open the file **build.gradle (modeule: app)**. Ensure that the **compileSdkVersion** and **buildToolsVersion** are set to the latest SDK version installed. The tags might look like this: compileSdkVersion 'Google Inc.:Google APIs:21' buildToolsVersion "21.1.2" 3. In the Android Studio Project Explorer right-click the project node, choose **Properties**, and in the left column choose **Android**. Ensure that the **Project Build Target** is set to the same SDK version as the **targetSdkVersion**. 4. In Android Studio, the manifest file is no longer used to specify the target SDK and minimum SDK version, unlike the case with Eclipse. ## Next steps This tutorial demonstrated the basics of enabling an Android app to work with data in Mobile Services. Try these other Android tutorials: * [Get started with authentication](mobile-services-android-get-started-users.md)
Learn how to authenticate users of your app. * [Get started with push notifications](mobile-services-javascript-backend-android-get-started-push.md)
Learn how to send a very basic push notification to your app with Mobile Services. [Download the Android app project]: #download-app [Create the mobile service]: #create-service [Add a data table for storage]: #add-table [Update the app to use Mobile Services]: #update-app [Test the app against Mobile Services]: #test-app [Next Steps]:#next-steps [8]: ./media/mobile-services-android-get-started-data/mobile-dashboard-tab.png [9]: ./media/mobile-services-android-get-started-data/mobile-todoitem-data-browse.png [12]: ./media/mobile-services-android-get-started-data/mobile-eclipse-project.png [13]: ./media/mobile-services-android-get-started-data/mobile-quickstart-startup-android.png [14]: ./media/mobile-services-android-get-started-data/mobile-services-import-android-workspace.png [15]: ./media/mobile-services-android-get-started-data/mobile-services-import-android-project.png [Mobile Services Android SDK]: http://aka.ms/Iajk6q [Azure classic portal]: https://manage.windowsazure.com/ [Azure Mobile Services Android SDK]: http://aka.ms/Iajk6q [GitHub]: http://go.microsoft.com/fwlink/p/?LinkID=282122 [Android SDK]: https://go.microsoft.com/fwLink/p/?LinkID=280125 ================================================ FILE: docs/mobile-services-android-get-started-offline-data.md ================================================ # Add Offline Data Sync to your Android Mobile Services app > [AZURE.SELECTOR] - [Android)](mobile-services-android-get-started-offline-data.md) - [iOS](mobile-services-ios-get-started-offline-data.md) - [Windows](mobile-services-windows-store-dotnet-get-started-offline-data.md) - [Xamarin.Android](mobile-services-xamarin-android-get-started-offline-data.md) - [Xamarin.iOS](mobile-services-xamarin-ios-get-started-offline-data.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Summary Mobile apps can lose network connectivity when moving to an area without service, or due to network issues. For example, a construction industry app used at a remote construction site might need to enter scheduling data that is synced up to Azure later. With Azure Mobile Services offline sync, you can keep working when network connectivity is lost, which is essential to many mobile apps. With offline sync, you work with a local copy of your Azure SQL Server table, and periodically re-sync the two. In this tutorial, you'll update the app from the [Mobile Services Quick Start tutorial] to enable offline sync, and then test the app by adding data offline, syncing those items to the online database, and verifying the changes in the Azure classic portal. Whether you are offline or connected, conflicts can arise any time multiple changes are made to data. A future tutorial will explore handling sync conflicts, where you choose which version of the changes to accept. In this tutorial, we assume no sync conflicts and any changes you make to existing data will be applied directly to the Azure SQL Server. ## What you need to get started This tutorial is based on the code you download in the Mobile Services quickstart. Before you start this tutorial, you must first complete either [Get started with Mobile Services] or [Add Mobile Services to an existing app]. > [AZURE.IMPORTANT] If you completed the quickstart tutorial prior to the release of Azure Mobile Services Android SDK 2.0, you must re-do it, because the SDK is not backwards compatible. To verify the version, check the **dependencies** section of your project's **build.gradle** file. ## Update the app to support offline sync With offline sync you read to and write from a *sync table* (using the *IMobileServiceSyncTable* interface), which is part of a **SQLite** database on your device. To push and pull changes between the device and Azure Mobile Services, you use a *synchronization context* (*MobileServiceClient.SyncContext*), which you initialize with the local database that you use to store data locally. 1. Add permission to check for network connectivity by adding this code to the *AndroidManifest.xml* file: 2. Add the following **import** statements to *ToDoActivity.java*: import java.util.Map; import android.widget.Toast; import com.microsoft.windowsazure.mobileservices.table.query.Query; import com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncContext; import com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncTable; import com.microsoft.windowsazure.mobileservices.table.sync.localstore.ColumnDataType; import com.microsoft.windowsazure.mobileservices.table.sync.localstore.SQLiteLocalStore; 3. Near the top of the `ToDoActivity` class, change the declaration of the `mToDoTable` variable from a `MobileServiceTable` class to a `MobileServiceSyncTable` class. private MobileServiceSyncTable mToDoTable; This is where you define the sync table. 4. To handle initialization of the local store, in the `onCreate` method, add the following lines after creating the `MobileServiceClient` instance: // Saves the query which will be used for pulling data mPullQuery = mClient.getTable(ToDoItem.class).where().field("complete").eq(false); SQLiteLocalStore localStore = new SQLiteLocalStore(mClient.getContext(), "ToDoItem", null, 1); SimpleSyncHandler handler = new SimpleSyncHandler(); MobileServiceSyncContext syncContext = mClient.getSyncContext(); Map tableDefinition = new HashMap(); tableDefinition.put("id", ColumnDataType.String); tableDefinition.put("text", ColumnDataType.String); tableDefinition.put("complete", ColumnDataType.Boolean); tableDefinition.put("__version", ColumnDataType.String); localStore.defineTable("ToDoItem", tableDefinition); syncContext.initialize(localStore, handler).get(); // Get the Mobile Service Table instance to use mToDoTable = mClient.getSyncTable(ToDoItem.class); 5. Following the preceding code, which is inside a `try` block, add an additional `catch` block following the `MalformedURLException` one: } catch (Exception e) { Throwable t = e; while (t.getCause() != null) { t = t.getCause(); } createAndShowDialog(new Exception("Unknown error: " + t.getMessage()), "Error"); } 6. Add this method to verify network connectivity: private boolean isNetworkAvailable() { ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); } 7. Add this method to sync between the local *SQL Light* store and the Azure SQL Server: public void syncAsync(){ if (isNetworkAvailable()) { new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mClient.getSyncContext().push().get(); mToDoTable.pull(mPullQuery).get(); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } else { Toast.makeText(this, "You are not online, re-sync later!" + "", Toast.LENGTH_LONG).show(); } } 8. In the `onCreate` method, add this code as the next-to-the-last line, right before the call to `refreshItemsFromTable`: syncAsync(); This causes the device on startup to sync with the Azure table. Otherwise you would display the last offline contents of the local store. 9. Update the code in the `refreshItemsFromTable` method to use this query (first line of code inside the `try` block): final MobileServiceList result = mToDoTable.read(mPullQuery).get(); 10. In the `onOptionsItemSelected` method replace the contents of the `if` block with this code: syncAsync(); refreshItemsFromTable(); This code runs when you press the **Refresh** button in the upper right corner. This is the main way you sync your local store to Azure, in addition to syncing at startup. This encourages batching of sync changes, and is efficient given that the pull from Azure is a relatively expensive operation. You could also design this app to sync on every change by adding a call to `syncAsync` to the `addItem` and `checkItem` methods, if your app required this. ## Test the app In this section, you will test the behavior with WiFi on, and then turn off WiFi to create an offline scenario. When you add data items, they are held in the local SQL Light store, but not synced to the mobile service until you press the **Refresh** button. Other apps may have different requirements regarding when data needs to be synchronized, but for demo purposes this tutorial has the user explicitly request it. When you press that button, a new background task starts, and first pushes all the changes made to the local store, by using the synchronization context, and then pulls all changed data from Azure to the local table. ### Online testing Lets test the following scenarios. 1. Add some new items on your device; 2. Verify the items don't show in the portal; 3. next press **Refresh** and verify that they then show up. 4. Change or add an item in the portal, then press **Refresh** and verify that the changes show up on your device. ### Offline testing 1. Place the device or simulator in *Airplane Mode*. This creates an offline scenario. 2. Add some *ToDo* items, or mark some items as complete. Quit the device or simulator (or forcibly close the app) and restart. Verify that your changes have been persisted on the device because they are held in the local SQL Light store. 3. View the contents of the Azure *TodoItem* table. Verify that the new items have _not_ been synced to the server: - For the JavaScript backend, go to the Azure classic portal, and click the Data tab to view the contents of the `TodoItem` table. - For the .NET backend, view the table contents either with a SQL tool such as *SQL Server Management Studio*, or a REST client such as *Fiddler* or *Postman*. 4. Turn on WiFi in the device or simulator. Next, press the **Refresh** button. 5. View the TodoItem data again in the Azure classic portal. The new and changed TodoItems should now appear. ## Next Steps * [Using Soft Delete in Mobile Services][Soft Delete] ## Additional Resources * [Cloud Cover: Offline Sync in Azure Mobile Services] * [Azure Friday: Offline-enabled apps in Azure Mobile Services] \(note: demos are for Windows, but feature discussion applies to all platforms\) [Get the sample app]: #get-app [Review the Core Data model]: #review-core-data [Review the Mobile Services sync code]: #review-sync [Change the sync behavior of the app]: #setup-sync [Test the app]: #test-app [Get started with Mobile Services]: mobile-services-android-get-started.md [Add Mobile Services to an existing app]: mobile-services-android-get-started-data.md [Mobile Services sample repository on GitHub]: https://github.com/Azure/mobile-services-samples [Handling Conflicts with Offline Support for Mobile Services]: mobile-services-android-handling-conflicts-offline-data.md [Soft Delete]: mobile-services-using-soft-delete.md [Cloud Cover: Offline Sync in Azure Mobile Services]: http://channel9.msdn.com/Shows/Cloud+Cover/Episode-155-Offline-Storage-with-Donna-Malayeri [Azure Friday: Offline-enabled apps in Azure Mobile Services]: http://azure.microsoft.com/documentation/videos/azure-mobile-services-offline-enabled-apps-with-donna-malayeri/ [Mobile Services Quick Start tutorial]: mobile-services-android-get-started.md ================================================ FILE: docs/mobile-services-android-get-started-users.md ================================================ # Add authentication to your Mobile Services Android app (JavaScript backend) > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Summary This topic shows you how to authenticate users in Azure Mobile Services from your app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. > [AZURE.VIDEO android-getting-started-with-authentication-in-windows-azure-mobile-services] This tutorial walks you through the basic steps to enable authentication in your app. ## Prerequisites This tutorial is based on the code you download in the Mobile Services quickstart. Before you start this tutorial, you must first complete either [Get started with Mobile Services] or [Add Mobile Services to an existing app]. > [AZURE.IMPORTANT] If you completed the quickstart tutorial prior to the release of Azure Mobile Services Android SDK 2.0, you must re-do it, because the SDK is not backwards compatible. To verify the version, check the **dependencies** section of your project's **build.gradle** file. ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario. 1. In Android Studio, open the project that you created when you completed the tutorial [Get started with Mobile Services]. 2. From the **Run** menu, then click **Run app**; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the _TodoItem_ table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app 1. In **Project Explorer** in Android Studio, open the ToDoActivity.java file and add the following import statements. import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import com.microsoft.windowsazure.mobileservices.authentication.MobileServiceAuthenticationProvider; import com.microsoft.windowsazure.mobileservices.authentication.MobileServiceUser; 2. Add the following method to the **ToDoActivity** class: private void authenticate() { // Login using the Google provider. ListenableFuture mLogin = mClient.login(MobileServiceAuthenticationProvider.Google); Futures.addCallback(mLogin, new FutureCallback() { @Override public void onFailure(Throwable exc) { createAndShowDialog((Exception) exc, "Error"); } @Override public void onSuccess(MobileServiceUser user) { createAndShowDialog(String.format( "You are now logged in - %1$2s", user.getUserId()), "Success"); createTable(); } }); } This creates a new method to handle the authentication process. The user is authenticated by using a Google login. A dialog is displayed which displays the ID of the authenticated user. You cannot proceed without a positive authentication. > [AZURE.NOTE] If you are using an identity provider other than Google, change the value passed to the **login** method above to one of the following: _MicrosoftAccount_, _Facebook_, _Twitter_, or _windowsazureactivedirectory_. 3. In the **onCreate** method, add the following line of code after the code that instantiates the `MobileServiceClient` object. authenticate(); This call starts the authentication process. 4. Move the remaining code after `authenticate();` in the **onCreate** method to a new **createTable** method, which looks like this: private void createTable() { // Get the table instance to use. mToDoTable = mClient.getTable(ToDoItem.class); mTextNewToDo = (EditText) findViewById(R.id.textNewToDo); // Create an adapter to bind the items with the view. mAdapter = new ToDoItemAdapter(this, R.layout.row_list_to_do); ListView listViewToDo = (ListView) findViewById(R.id.listViewToDo); listViewToDo.setAdapter(mAdapter); // Load the items from Azure. refreshItemsFromTable(); } 9. From the **Run** menu, then click **Run app** to start the app and sign in with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query the backend service and make updates to data. ## Cache authentication tokens on the client The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the backend Azure service every time that the app starts. Not only is this method inefficient, you can run into usage-related issues should many customers try to start you app at the same time. A better approach is to cache the authorization token returned by the Azure service and try to use this first before using a provider-based sign-in. >[AZURE.NOTE]You can cache the token issued by the backend Azure service regardless of whether you are using client-managed or service-managed authentication. This tutorial uses service-managed authentication. 1. Open the ToDoActivity.java file and add the following import statements: import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; 2. Add the the following members to the `ToDoActivity` class. public static final String SHAREDPREFFILE = "temp"; public static final String USERIDPREF = "uid"; public static final String TOKENPREF = "tkn"; 3. In the ToDoActivity.java file, add the the following definition for the `cacheUserToken` method. private void cacheUserToken(MobileServiceUser user) { SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE); Editor editor = prefs.edit(); editor.putString(USERIDPREF, user.getUserId()); editor.putString(TOKENPREF, user.getAuthenticationToken()); editor.commit(); } This method stores the user id and token in a preference file that is marked private. This should protect access to the cache so that other apps on the device do not have access to the token because the preference is sandboxed for the app. However, if someone gains access to the device, it is possible that they may gain access to the token cache through other means. >[AZURE.NOTE]You can further protect the token with encryption if token access to your data is considered highly sensitive and someone may gain access to the device. However, a completely secure solution is beyond the scope of this tutorial and dependent on your security requirements. 4. In the ToDoActivity.java file, add the the following definition for the `loadUserTokenCache` method. private boolean loadUserTokenCache(MobileServiceClient client) { SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE); String userId = prefs.getString(USERIDPREF, null); if (userId == null) return false; String token = prefs.getString(TOKENPREF, null); if (token == null) return false; MobileServiceUser user = new MobileServiceUser(userId); user.setAuthenticationToken(token); client.setCurrentUser(user); return true; } 5. In the *ToDoActivity.java* file, replace the `authenticate` method with the following method which uses a token cache. Change the login provider if you want to use an account other than Google. private void authenticate() { // We first try to load a token cache if one exists. if (loadUserTokenCache(mClient)) { createTable(); } // If we failed to load a token cache, login and create a token cache else { // Login using the Google provider. ListenableFuture mLogin = mClient.login(MobileServiceAuthenticationProvider.Google); Futures.addCallback(mLogin, new FutureCallback() { @Override public void onFailure(Throwable exc) { createAndShowDialog("You must log in. Login Required", "Error"); } @Override public void onSuccess(MobileServiceUser user) { createAndShowDialog(String.format( "You are now logged in - %1$2s", user.getUserId()), "Success"); cacheUserToken(mClient.getCurrentUser()); createTable(); } }); } } 6. Build the app and test authentication using a valid account. Run it at least twice. During the first run, you should receive a prompt to login and create the token cache. After that, each run will attempt to load the token cache for authentication and you should not be required to login. ## Refresh the token cache Our token cache should work in a simple case but, what happens when the token expires or is revoked? The token could expire when the app is not running. This would mean the token cache is invalid. The token could also expire while the app is actually running. The result is an HTTP status code 401 "Unauthorized". We need to be able to detect an expired token, and refresh it. To do this we use a [ServiceFilter](http://dl.windowsazure.com/androiddocs/com/microsoft/windowsazure/mobileservices/ServiceFilter.html) from the [Android client library](http://dl.windowsazure.com/androiddocs/). In this section you will define a ServiceFilter that will detect a HTTP status code 401 response and trigger a refresh of the token and the token cache. Additionally, this ServiceFilter will block other outbound requests during authentication so that those requests can use the refreshed token. 1. Open the ToDoActivity.java file and add the following import statements: import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.ExecutionException; import com.microsoft.windowsazure.mobileservices.MobileServiceException; 2. Add the following members to the `ToDoActivity` class. public boolean bAuthenticating = false; public final Object mAuthenticationLock = new Object(); These will be used to help synchronize the authentication of the user. We only want to authenticate once. Any calls during an authentication should wait and use the new token from the authentication in progress. 3. In the ToDoActivity.java file, add the following method to the ToDoActivity class that will be used to block outbound calls on other threads while authentication is in progress. /** * Detects if authentication is in progress and waits for it to complete. * Returns true if authentication was detected as in progress. False otherwise. */ public boolean detectAndWaitForAuthentication() { boolean detected = false; synchronized(mAuthenticationLock) { do { if (bAuthenticating == true) detected = true; try { mAuthenticationLock.wait(1000); } catch(InterruptedException e) {} } while(bAuthenticating == true); } if (bAuthenticating == true) return true; return detected; } 4. In the ToDoActivity.java file, add the following method to the ToDoActivity class. This method triggers the wait and then update the token on outbound requests when authentication is complete. /** * Waits for authentication to complete then adds or updates the token * in the X-ZUMO-AUTH request header. * * @param request * The request that receives the updated token. */ private void waitAndUpdateRequestToken(ServiceFilterRequest request) { MobileServiceUser user = null; if (detectAndWaitForAuthentication()) { user = mClient.getCurrentUser(); if (user != null) { request.removeHeader("X-ZUMO-AUTH"); request.addHeader("X-ZUMO-AUTH", user.getAuthenticationToken()); } } } 5. In the ToDoActivity.java file, update the `authenticate` method of the ToDoActivity class so that it accepts a boolean parameter to allow forcing the refresh of the token and token cache. We also need to notify any blocked threads when authentication is completed so they can pick up the new token. /** * Authenticates with the desired login provider. Also caches the token. * * If a local token cache is detected, the token cache is used instead of an actual * login unless bRefresh is set to true forcing a refresh. * * @param bRefreshCache * Indicates whether to force a token refresh. */ private void authenticate(boolean bRefreshCache) { bAuthenticating = true; if (bRefreshCache || !loadUserTokenCache(mClient)) { // New login using the provider and update the token cache. mClient.login(MobileServiceAuthenticationProvider.MicrosoftAccount, new UserAuthenticationCallback() { @Override public void onCompleted(MobileServiceUser user, Exception exception, ServiceFilterResponse response) { synchronized(mAuthenticationLock) { if (exception == null) { cacheUserToken(mClient.getCurrentUser()); createTable(); } else { createAndShowDialog(exception.getMessage(), "Login Error"); } bAuthenticating = false; mAuthenticationLock.notifyAll(); } } }); } else { // Other threads may be blocked waiting to be notified when // authentication is complete. synchronized(mAuthenticationLock) { bAuthenticating = false; mAuthenticationLock.notifyAll(); } createTable(); } } 6. In the ToDoActivity.java file, add this code for a new `RefreshTokenCacheFilter` class inside the ToDoActivity class: /** * The RefreshTokenCacheFilter class filters responses for HTTP status code 401. * When 401 is encountered, the filter calls the authenticate method on the * UI thread. Out going requests and retries are blocked during authentication. * Once authentication is complete, the token cache is updated and * any blocked request will receive the X-ZUMO-AUTH header added or updated to * that request. */ private class RefreshTokenCacheFilter implements ServiceFilter { AtomicBoolean mAtomicAuthenticatingFlag = new AtomicBoolean(); @Override public ListenableFuture handleRequest( final ServiceFilterRequest request, final NextServiceFilterCallback nextServiceFilterCallback ) { // In this example, if authentication is already in progress we block the request // until authentication is complete to avoid unnecessary authentications as // a result of HTTP status code 401. // If authentication was detected, add the token to the request. waitAndUpdateRequestToken(request); // Send the request down the filter chain // retrying up to 5 times on 401 response codes. ListenableFuture future = null; ServiceFilterResponse response = null; int responseCode = 401; for (int i = 0; (i < 5 ) && (responseCode == 401); i++) { future = nextServiceFilterCallback.onNext(request); try { response = future.get(); responseCode = response.getStatus().getStatusCode(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { if (e.getCause().getClass() == MobileServiceException.class) { MobileServiceException mEx = (MobileServiceException) e.getCause(); responseCode = mEx.getResponse().getStatus().getStatusCode(); if (responseCode == 401) { // Two simultaneous requests from independent threads could get HTTP status 401. // Protecting against that right here so multiple authentication requests are // not setup to run on the UI thread. // We only want to authenticate once. Requests should just wait and retry // with the new token. if (mAtomicAuthenticatingFlag.compareAndSet(false, true)) { // Authenticate on UI thread runOnUiThread(new Runnable() { @Override public void run() { // Force a token refresh during authentication. authenticate(true); } }); } // Wait for authentication to complete then update the token in the request. waitAndUpdateRequestToken(request); mAtomicAuthenticatingFlag.set(false); } } } } return future; } } This service filter will check each response for HTTP status code 401 "Unauthorized". If a 401 is encountered, a new login request to obtain a new token will be setup on the UI thread. Other calls will be blocked until the login is completed, or until 5 attempts have failed. If the new token is obtained, the request that triggered the 401 will be retried with the new token and any blocked calls will be retried with the new token. 7. In the ToDoActivity.java file, add this code for a new `ProgressFilter` class inside the ToDoActivity class: /** * The ProgressFilter class renders a progress bar on the screen during the time the App is waiting for the response of a previous request. * the filter shows the progress bar on the beginning of the request, and hides it when the response arrived. */ private class ProgressFilter implements ServiceFilter { @Override public ListenableFuture handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilterCallback) { final SettableFuture resultFuture = SettableFuture.create(); runOnUiThread(new Runnable() { @Override public void run() { if (mProgressBar != null) mProgressBar.setVisibility(ProgressBar.VISIBLE); } }); ListenableFuture future = nextServiceFilterCallback.onNext(request); Futures.addCallback(future, new FutureCallback() { @Override public void onFailure(Throwable e) { resultFuture.setException(e); } @Override public void onSuccess(ServiceFilterResponse response) { runOnUiThread(new Runnable() { @Override public void run() { if (mProgressBar != null) mProgressBar.setVisibility(ProgressBar.GONE); } }); resultFuture.set(response); } }); return resultFuture; } } This filter will show the progress bar on the beginning of the request and will hide it when the response arrived. 8. In the ToDoActivity.java file, update the `onCreate` method as follows: @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_to_do); mProgressBar = (ProgressBar) findViewById(R.id.loadingProgressBar); // Initialize the progress bar mProgressBar.setVisibility(ProgressBar.GONE); try { // Create the Mobile Service Client instance, using the provided // Mobile Service URL and key mClient = new MobileServiceClient( "https://.azure-mobile.net/", "", this) .withFilter(new ProgressFilter()) .withFilter(new RefreshTokenCacheFilter()); // Authenticate passing false to load the current token cache if available. authenticate(false); } catch (MalformedURLException e) { createAndShowDialog(new Exception("Error creating the Mobile Service. " + "Verify the URL"), "Error"); } } In this code, `RefreshTokenCacheFilter` is used in addition to `ProgressFilter`. Also during `onCreate` we want to load the token cache. So `false` is passed in to the `authenticate` method. ## Next steps In the next tutorial, [Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Store authentication tokens on the client]: #cache-tokens [Refresh expired tokens]: #refresh-tokens [Next Steps]:#next-steps [4]: ./media/mobile-services-android-get-started-users/mobile-services-selection.png [5]: ./media/mobile-services-android-get-started-users/mobile-service-uri.png [13]: ./media/mobile-services-android-get-started-users/mobile-identity-tab.png [14]: ./media/mobile-services-android-get-started-users/mobile-portal-data-tables.png [15]: ./media/mobile-services-android-get-started-users/mobile-portal-change-table-perms.png [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-android-get-started.md [Authorize users with scripts]: mobile-services-javascript-backend-service-side-authorization.md ================================================ FILE: docs/mobile-services-android-get-started.md ================================================ # Get started with Mobile Services for Android (JavaScript backend) > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to an Android app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple **To do list** app that stores app data in the new mobile service. > [AZURE.VIDEO mobile-get-started-android] A screenshot from the completed app is below: ![](./media/mobile-services-android-get-started/mobile-quickstart-completed-android.png) ## Prerequisites Completing this tutorial requires the [Android Developer Tools](https://developer.android.com/sdk/index.html), which includes the Android Studio integrated development environment, and the latest Android platform. Android 4.2 or a later version is required. The downloaded quickstart project contains the Azure Mobile Services SDK for Android. > [AZURE.IMPORTANT] To complete this tutorial, you need an Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=AE564AB28). ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Android app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will create a new Android app that is connected to your mobile service. 1. In the Azure classic portal, click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Android** under **Choose platform** and expand **Create a new Android app**. ![](./media/mobile-services-android-get-started/mobile-portal-quickstart-android1.png) This displays the three easy steps to create an Android app connected to your mobile service. ![](./media/mobile-services-android-get-started/mobile-quickstart-steps-android-AS.png) 3. If you haven't already done so, download and install the [Android Developer Tools](https://go.microsoft.com/fwLink/p/?LinkID=280125) on your local computer or virtual machine. 4. Click **Create TodoItem table** to create a table to store app data. 5. Now download your app by pressing the **Download** button. ## Run your Android app The final stage of this tutorial is to build and run your new app. ### Load project into Android Studio and sync Gradle 1. Browse to the location where you saved the compressed project files and expand the files on your computer into your Android Studio projects directory. 2. Open Android Studio. If you are working with a project and it appears, close the project (File => Close Project). 3. Select **Open an existing Android Studio project**, browse to the project location, and then click **OK.** This will load the project and start to sync it with Gradle. ![](./media/mobile-services-android-get-started/android-studio-import-project.png) 4. Wait for the Gradle sync activity to complete. If you see a "failed to find target" error, this is because the version used in Android Studio doesn't match that of the sample. The easiest way to fix this is to click the **Install missing platform(s) and sync project** link in the error message. You might get additional version error messages, and you simply repeat this process until no errors appear. - There is another way to fix this if you want to run with the "latest and greatest" version of Android. You can update the **targetSdkVersion** in the *build.gradle* file in the *app* directory to match the version already installed on your machine, which you can discover by clicking the **SDK Manager** icon and seeing what version is listed. Next you press the **Sync Project with Gradle Files**. You may get an error message for the version of Build Tools, and you fix that the same way. ### Running the app You can run the app using the emulator, or using an actual device. 1. To run from a device, connect it to your computer with a USB cable. You must [set up the device for development](https://developer.android.com/training/basics/firstapp/running-app.html). If you are developing on a Windows machine, you must also download and install a USB driver. 2. To run using the Android emulator, you must define at least one Android Virtual Device (AVD). Click the AVD Manager icon to create and manage these devices. 3. From the **Run** menu, click **Run** to start the project. and choose a device or emulator from the dialog box that appears. 4. When the app appears, type meaningful text, such as _Complete the tutorial_, and then click **Add**. ![](./media/mobile-services-android-get-started/mobile-quickstart-startup-android.png) This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. > [AZURE.NOTE] You can review the code that accesses your mobile service to query and insert data, which is found in the ToDoActivity.java file. 8. Back in the Azure classic portal, click the **Data** tab and then click the **TodoItems** table. ![](./media/mobile-services-android-get-started/mobile-data-tab1.png) This lets you browse the data inserted by the app into the table. ![](./media/mobile-services-android-get-started/mobile-data-browse.png) ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with data]
Learn more about storing and querying data using Mobile Services. * [Get started with authentication]
Learn how to authenticate users of your app with an identity provider. * [Get started with push notifications]
Learn how to send a very basic push notification to your app. [Get started (Eclipse)]: mobile-services-android-get-started-ec.md [Get started with data]: mobile-services-android-get-started-data.md [Get started with authentication]: mobile-services-android-get-started-users.md [Get started with push notifications]: mobile-services-javascript-backend-android-get-started-push.md [Mobile Services Android SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 ================================================ FILE: docs/mobile-services-android-how-to-use-client-library.md ================================================ # How to use the Android client library for Mobile Services > [AZURE.SELECTOR] - [Android](mobile-services-android-how-to-use-client-library.md) - [HTML/JavaScript](mobile-services-html-how-to-use-client-library.md) - [iOS](mobile-services-ios-how-to-use-client-library.md) - [Managed (Windows/Xamarin)](mobile-services-dotnet-how-to-use-client-library.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This guide shows you how to perform common scenarios using the Android client for Azure Mobile Services. The scenarios covered include querying for data; inserting, updating, and deleting data, authenticating users, handling errors, and customizing the client. If you are new to Mobile Services, you should first complete the quickstart tutorial [Get started with Mobile Services]. Successfully completing that tutorial ensures that you will have installed Android Studio; it will help you configure your account and create your first mobile service, and install the Mobile Services SDK, which supports Android version 2.2 or later, but we recommend building against Android version 4.2 or later. You can find the Javadocs API reference for the Android client library [here](http://go.microsoft.com/fwlink/p/?LinkId=298735). ## What is Mobile Services Azure Mobile Services is a highly scalable mobile application development platform that lets you add enhanced functionality to your mobile device apps by using Azure. With Mobile Services you can: + **Build native and cross platform apps** - Connect your iOS, Android, Windows, or cross-platform Xamarin or Cordova (Phonegap) apps to your backend mobile service using native SDKs. + **Send push notifications to your users** - Send push notifications to your users of your app. + **Authenticate your users** - Leverage popular identity providers like Facebook and Twitter to authenticate your app users. + **Store data in the cloud** - Store user data in a SQL Database (by default) or in Mongo DB, DocumentDB, Azure Tables, or Azure Blobs. + **Build offline-ready apps with sync** - Make your apps work offline and use Mobile Services to sync data in the background. + **Monitor and scale your apps** - Monitor app usage and scale your backend as demand grows. ## Mobile Services Concepts The following are important features and concepts in the Mobile Services: + **Application key:** a unique value that is used to limit access to your mobile service from random clients; this "key" is not a security token and is not used to authenticate users of your app. + **Backend:** the mobile service instance that supports your app. A mobile service is implemented either as an ASP.NET Web API project (*.NET backend* ) or as a Node.js project (*JavaScript backend*). + **Identity provider:** an external service, trusted by Mobile Services, that authenticates your app's users. Supported providers include: Facebook, Twitter, Google, Microsoft Account, and Azure Active Directory. + **Push notification:** Service-initiated message that is sent to a registered device or user using Azure Notification Hubs. + **Scale:** The ability to add, for an additional cost, more processing power, performance, and storage as your app becomes more popular. + **Scheduled job:** Custom code that is run either on a pre-determined schedule or on-demand. For more information, see [Mobile Services Concepts](./ mobile-services-concepts-links.md). ## Setup and Prerequisites We assume that you have created a mobile service and a table. For more information see [Create a table](http://go.microsoft.com/fwlink/p/?LinkId=298592). In the code used in this topic, we assume the table is named *ToDoItem*, and that it has the following columns: - id - text - complete The corresponding typed client side object is the following: public class ToDoItem { private String id; private String text; private Boolean complete; } When dynamic schema is enabled, Azure Mobile Services automatically generates new columns based on the object in the insert or update request. For more information, see [Dynamic schema](http://go.microsoft.com/fwlink/p/?LinkId=296271). ## How to: Create the Mobile Services client The following code creates the [MobileServiceClient](http://dl.windowsazure.com/androiddocs/com/microsoft/windowsazure/mobileservices/MobileServiceClient.html) object that is used to access your mobile service. The code goes in the `onCreate` method of the Activity class specified in *AndroidManifest.xml* as a **MAIN** action and **LAUNCHER** category. MobileServiceClient mClient = new MobileServiceClient( "MobileServiceUrl", // Replace with the above Site URL "AppKey", // replace with the Application Key this) In the code above, replace `MobileServiceUrl` and `AppKey` with the mobile service URL and application key, in that order. Both of these are available on the Azure classic portal, by selecting your mobile service and then clicking on *Dashboard*. ## How to: Create a table reference The easiest way to query or modify data in the mobile service is by using the *typed programming model*, since Java is a strongly typed language (later on we will discuss the *untyped* model). This model provides seamless serialization and deserialization to JSON using the [gson](http://go.microsoft.com/fwlink/p/?LinkId=290801) library when sending data between the client and the mobile service: the developer doesn't have to do anything, the framework handles it all. The first thing you do to query or modify data is to create a [MobileServiceTable](http://go.microsoft.com/fwlink/p/?LinkId=296835) object by calling the **getTable** method on the [**MobileServiceClient**](http://dl.windowsazure.com/androiddocs/com/microsoft/windowsazure/mobileservices/MobileServiceClient.html). We will look at two overloads of this method: public class MobileServiceClient { public MobileServiceTable getTable(Class clazz); public MobileServiceTable getTable(String name, Class clazz); } In the following code, *mClient* is a reference to your mobile service client. The [first overload](http://go.microsoft.com/fwlink/p/?LinkId=296839) is used where the class name and the table name are the same: MobileServiceTable mToDoTable = mClient.getTable(ToDoItem.class); The [2nd overload](http://go.microsoft.com/fwlink/p/?LinkId=296840) is used when the table name is different from the type name. MobileServiceTable mToDoTable = mClient.getTable("ToDoItemBackup", ToDoItem.class); ## The API structure Since version 2.0 of the client library, mobile services table operations use the [Future](http://developer.android.com/reference/java/util/concurrent/Future.html) and [AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html) objects in all of the asynchronous operations such as methods involving queries and operations like inserts, updates and deletes. This makes it easier to perform multiple operations (while on a background thread) without having to deal with multiple nested callbacks. ## How to: Query data from a mobile service This section describes how to issue queries to the mobile service. Subsections describe diffent aspects such as sorting, filtering, and paging. Finally, we discuss how you can concatenate these operations together. ### How to: Return all Items from a Table The following code returns all items in the *ToDoItem* table. It displays them in the UI by adding the items to an adapter. This code is similar to what is in the the quickstart tutorial [Get started with Mobile Services]. new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final MobileServiceList result = mToDoTable.execute().get(); runOnUiThread(new Runnable() { @Override public void run() { mAdapter.clear(); for (ToDoItem item : result) { mAdapter.add(item); } } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return result; } }.execute(); Queries like this one use the [AsyncTask](http://developer.android.com/reference/android/os/AsyncTask.html) object. The *result* variable returns the result set from the query, and the code following the `mToDoTable.execute().get()` statement shows how to display the individual rows. ### How to: Filter returned data The following code returns all items from the *ToDoItem* table whose *complete* field equals *false*. *mToDoTable* is the reference to the mobile service table that we created previously. new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final MobileServiceList result = mToDoTable.where().field("complete").eq(false).execute().get(); for (ToDoItem item : result) { Log.i(TAG, "Read object with ID " + item.id); } } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); You start a filter with a [**where**](http://go.microsoft.com/fwlink/p/?LinkId=296867) method call on the table reference. This is followed by a [**field**](http://go.microsoft.com/fwlink/p/?LinkId=296869) method call followed by a method call that specifies the logical predicate. Possible predicate methods include [**eq**](http://go.microsoft.com/fwlink/p/?LinkId=298461), [**ne**](http://go.microsoft.com/fwlink/p/?LinkId=298462), [**gt**](http://go.microsoft.com/fwlink/p/?LinkId=298463), [**ge**](http://go.microsoft.com/fwlink/p/?LinkId=298464), [**lt**](http://go.microsoft.com/fwlink/p/?LinkId=298465), [**le**](http://go.microsoft.com/fwlink/p/?LinkId=298466) etc. This is sufficient for comparing number and string fields to specific values. But you can do a lot more. For example, you can filter on dates. You can compare the entire date field, but you can also compare parts of the date, with methods such as [**year**](http://go.microsoft.com/fwlink/p/?LinkId=298467), [**month**](http://go.microsoft.com/fwlink/p/?LinkId=298468), [**day**](http://go.microsoft.com/fwlink/p/?LinkId=298469), [**hour**](http://go.microsoft.com/fwlink/p/?LinkId=298470), [**minute**](http://go.microsoft.com/fwlink/p/?LinkId=298471) and [**second**](http://go.microsoft.com/fwlink/p/?LinkId=298472). The following partial code adds a filter for items whose *due date* equals 2013. mToDoTable.where().year("due").eq(2013).execute().get(); You can do a wide variety of complex filters on string fields with methods like [**startsWith**](http://go.microsoft.com/fwlink/p/?LinkId=298473), [**endsWith**](http://go.microsoft.com/fwlink/p/?LinkId=298474), [**concat**](http://go.microsoft.com/fwlink/p/?LinkId=298475), [**subString**](http://go.microsoft.com/fwlink/p/?LinkId=298477), [**indexOf**](http://go.microsoft.com/fwlink/p/?LinkId=298488), [**replace**](http://go.microsoft.com/fwlink/p/?LinkId=298491), [**toLower**](http://go.microsoft.com/fwlink/p/?LinkId=298492), [**toUpper**](http://go.microsoft.com/fwlink/p/?LinkId=298493), [**trim**](http://go.microsoft.com/fwlink/p/?LinkId=298495), and [**length**](http://go.microsoft.com/fwlink/p/?LinkId=298496). The following partial code filters for table rows where the *text* column starts with "PRI0". mToDoTable.where().startsWith("text", "PRI0").execute().get(); Number fields also allow a wide variety of more complex filters with methods like [**add**](http://go.microsoft.com/fwlink/p/?LinkId=298497), [**sub**](http://go.microsoft.com/fwlink/p/?LinkId=298499), [**mul**](http://go.microsoft.com/fwlink/p/?LinkId=298500), [**div**](http://go.microsoft.com/fwlink/p/?LinkId=298502), [**mod**](http://go.microsoft.com/fwlink/p/?LinkId=298503), [**floor**](http://go.microsoft.com/fwlink/p/?LinkId=298505), [**ceiling**](http://go.microsoft.com/fwlink/p/?LinkId=298506), and [**round**](http://go.microsoft.com/fwlink/p/?LinkId=298507). The following partial code filters for table rows where the *duration* is an even number. mToDoTable.where().field("duration").mod(2).eq(0).execute().get(); You can combine predicates with methods like [**and**](http://go.microsoft.com/fwlink/p/?LinkId=298512), [**or**](http://go.microsoft.com/fwlink/p/?LinkId=298514) and [**not**](http://go.microsoft.com/fwlink/p/?LinkId=298515). This partial code combines two of the above examples. mToDoTable.where().year("due").eq(2013).and().startsWith("text", "PRI0") .execute().get(); And you can group and nest logical operators, as shown in this partial code: mToDoTable.where() .year("due").eq(2013) .and (startsWith("text", "PRI0").or().field("duration").gt(10)) .execute().get(); For more detailed discussion and examples of filtering, see [Exploring the richness of the Mobile Services Android client query model](http://hashtagfail.com/post/46493261719/mobile-services-android-querying). ### How to: Sort returned data The following code returns all items from a table of *ToDoItems* sorted ascending by the *text* field. *mToDoTable* is the reference to the mobile mervice table that you created previously. mToDoTable.orderBy("text", QueryOrder.Ascending).execute().get(); The first parameter of the [**orderBy**](http://go.microsoft.com/fwlink/p/?LinkId=298519) method is a string equal to the name of the field on which to sort. The second parameter uses the [**QueryOrder**](http://go.microsoft.com/fwlink/p/?LinkId=298521) enumeration to specify whether to sort ascending or descending. Note that if you are filtering using the ***where*** method, the ***where*** method must be invoked prior to the ***orderBy*** method. ### How to: Return data in pages The first example shows how to select the top 5 items from a table. The query returns the items from a table of *ToDoItems*. *mToDoTable* is the reference to the mobile service table that you created previously. final MobileServiceList result = mToDoTable.top(5).execute().get(); Next, we define a query that skips the first 5 items, and then returns the next 5. mToDoTable.skip(5).top(5).execute().get(); ### How to: Select specific columns The following code illustrates how to return all items from a table of *ToDoItems*, but only displays the *complete* and *text* fields. *mToDoTable* is the reference to the mobile service table that we created previously. mToDoTable.select("complete", "text").execute().get(); Here the parameters to the select function are the string names of the table's columns that you want to return. The [**select**](http://go.microsoft.com/fwlink/p/?LinkId=290689) method needs to follow methods like [**where**](http://go.microsoft.com/fwlink/p/?LinkId=296296) and [**orderBy**](http://go.microsoft.com/fwlink/p/?LinkId=296313), if they are present. It can be followed by methods like [**top**](http://go.microsoft.com/fwlink/p/?LinkId=298731). ### How to: Concatenate query methods The methods used in querying mobile mervice tables can be concatenated. This allows you to do things like select specific columns of filtered rows that are sorted and paged. You can create quite complex logical filters. What makes this work is that the query methods you use return [**MobileServiceQuery<T>**](http://go.microsoft.com/fwlink/p/?LinkId=298551) objects, which can in turn have additional methods invoked on them. To end the series of methods and actually run the query, you call the [**execute**](http://go.microsoft.com/fwlink/p/?LinkId=298554) method. Here's a code sample where *mToDoTable* is a reference to the mobile services *ToDoItem* table. mToDoTable.where().year("due").eq(2013) .and().startsWith("text", "PRI0") .or().field("duration").gt(10) .select("id", "complete", "text", "duration") .orderBy(duration, QueryOrder.Ascending).top(20) .execute().get(); The main requirement in chaining methods together is that the *where* method and predicates need to come first. After that, you can call subsequent methods in the order that best meets the needs of your application. ## How to: Insert data into a mobile service The following code shows how to insert a new row into a table. First you instantiate an instance of the *ToDoItem* class and set its properties. ToDoItem mToDoItem = new ToDoItem(); mToDoItem.text = "Test Program"; mToDoItem.complete = false; Next you execute the following code: // Insert the new item new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.insert(item).get(); if (!item.isComplete()) { runOnUiThread(new Runnable() { public void run() { mAdapter.add(item); } }); } } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); This code inserts the new item, and adds it to the adapter so it displays in the UI. Mobile Services supports unique custom string values for the table id. This allows applications to use custom values such as email addresses or usernames for the id column of a Mobile Services table. For example if you wanted to identify each record by an email address, you could use the following JSON object. ToDoItem mToDoItem = new ToDoItem(); mToDoItem.id = "myemail@mydomain.com"; mToDoItem.text = "Test Program"; mToDoItem.complete = false; If a string id value is not provided when inserting new records into a table, Mobile Services will generate a unique value for the id. Supporting string ids provides the following advantages to developers + Ids can be generated without making a roundtrip to the database. + Records are easier to merge from different tables or databases. + Ids values can integrate better with an application's logic. You can also use server scripts to set id values. The script example below generates a custom GUID and assigns it to a new record's id. This is similar to the id value that Mobile Services would generate if you didn't pass in a value for a record's id. //Example of generating an id. This is not required since Mobile Services //will generate an id if one is not passed in. item.id = item.id || newGuid(); request.execute(); function newGuid() { var pad4 = function(str) { return "0000".substring(str.length) + str; }; var hex4 = function () { return pad4(Math.floor(Math.random() * 0x10000 /* 65536 */ ).toString(16)); }; return (hex4() + hex4() + "-" + hex4() + "-" + hex4() + "-" + hex4() + "-" + hex4() + hex4() + hex4()); } If an application provides a value for an id, Mobile Services will store it as is. This includes leading or trailing white spaces. White space will not be trimmed from value. The value for the `id` must be unique and it must not include characters from the following sets: + Control characters: [0x0000-0x001F] and [0x007F-0x009F]. For more information, see [ASCII control codes C0 and C1]. + Printable characters: **"**(0x0022), **\+** (0x002B), **/** (0x002F), **?** (0x003F), **\\** (0x005C), **`** (0x0060) + The ids "." and ".." You can alternatively use integer Ids for your tables. In order to use an integer Id you must create your table with the `mobile table create` command using the `--integerId` option. This command is used with the Command-line Interface (CLI) for Azure. For more information on using the CLI, see [CLI to manage Mobile Services tables]. ## How to: Update data in a mobile service The following code shows how to update data in a table. In this example, *item* is a reference to a row in the *ToDoItem* table, which has had some changes made to it. The following method updates the table and the UI adapter. private void updateItem(final ToDoItem item) { if (mClient == null) { return; } new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.update(item).get(); runOnUiThread(new Runnable() { public void run() { if (item.isComplete()) { mAdapter.remove(item); } refreshItemsFromTable(); } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } ## How to: Delete data in a mobile service The following code shows how to delete data from a table. It deletes an existing item from the ToDoItem table that has had the **Completed** check box on the UI checked. public void checkItem(final ToDoItem item) { if (mClient == null) { return; } // Set the item as completed and update it in the table item.setComplete(true); new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.delete(item); runOnUiThread(new Runnable() { public void run() { if (item.isComplete()) { mAdapter.remove(item); } refreshItemsFromTable(); } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } The following code illustrates another way to do this. It deletes an existing item in the ToDoItem table by specifying the value of the id field of the row to delete (assumed to equal "2FA404AB-E458-44CD-BC1B-3BC847EF0902"). In an actual app you would pick up the ID somehow and pass it in as a variable. Here, to simplify testing, you can go to your service in the Azure classic portal, click **Data** and copy an ID that you wish to test with. public void deleteItem(View view) { final String ID = "2FA404AB-E458-44CD-BC1B-3BC847EF0902"; new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mToDoTable.delete(ID); runOnUiThread(new Runnable() { public void run() { refreshItemsFromTable(); } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } ## How to: Look up a specific item Sometimes you want to look up a specific item by its *id*, unlike querying where you typically get a collection of items that satisfy some criteria. The following code shows how to do this, for an *id* value of `0380BAFB-BCFF-443C-B7D5-30199F730335`. In an actual app you would pick up the ID somehow and pass it in as a variable. Here, to simplify testing, you can go to your service in the Azure classic portal, click the **Data** tab and copy an ID that you wish to test with. /** * Lookup specific item from table and UI */ public void lookup(View view) { final String ID = "0380BAFB-BCFF-443C-B7D5-30199F730335"; new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final ToDoItem result = mToDoTable.lookUp(ID).get(); runOnUiThread(new Runnable() { public void run() { mAdapter.clear(); mAdapter.add(result); } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } ## How to: Work with untyped data The untyped programming model gives you exact control over the JSON serialization, and there are some scenarios where you may wish to use it, for example, if your mobile service table contains a large number of columns and you only need to reference a few of them. Using the typed model requires you to define all of the mobile service table's columns in your data class. But with the untyped model you only define the columns you need to use. Most of the API calls for accessing data are similar to the typed programming calls. The main difference is that in the untyped model you invoke methods on the **MobileServiceJsonTable** object, instead of the **MobileServiceTable** object. ### How to: Create an instance of an untyped table Similar to the typed model, you start by getting a table reference, but in this case it's a [MobileServicesJsonTable](http://go.microsoft.com/fwlink/p/?LinkId=298733) object. You get the reference by calling the [getTable()](http://go.microsoft.com/fwlink/p/?LinkId=298734) method on an instance of the Mobile Services client. First you define the variable: /** * Mobile Service Json Table used to access untyped data */ private MobileServiceJsonTable mJsonToDoTable; Once you create an instance of the Mobile Services client in the **onCreate** method (here, the *mClient* variable), you next create an instance of a **MobileServiceJsonTable**, with the following code. // Get the Mobile Service Json Table to use mJsonToDoTable = mClient.getTable("ToDoItem"); Once you have created an instance of the **MobileServiceJsonTable**, you can call almost all of the methods on it that you can with the typed programming model. However in some cases the methods take an untyped parameter, as we see in the following examples. ### How to: Insert into an untyped table The following code shows how to do an insert. The first step is to create a [**JsonObject**](http://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/JsonObject.html), which is part of the gson library. JsonObject item = new JsonObject(); item.addProperty("text", "Wake up"); item.addProperty("complete", false); The next step is to insert the object. The callback function passed to the [**insert**](http://go.microsoft.com/fwlink/p/?LinkId=298535) method is an instance of the [**TableJsonOperationCallback**](http://go.microsoft.com/fwlink/p/?LinkId=298532) class. Note how the parameter of the *insert* method is a JsonObject. // Insert the new item new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { mJsonToDoTable.insert(item).get(); refreshItemsFromTable(); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); If you need to get the ID of the inserted object, use this method call: jsonObject.getAsJsonPrimitive("id").getAsInt()); ### How to: Delete from an untyped table The following code shows how to delete an instance, in this case, the same instance of a **JsonObject** that was created in the prior *insert* example. Note that the code is the same as with the typed case, but the method has a different signature since it references an **JsonObject**. mToDoTable.delete(item); You can also delete an instance directly by using its ID: mToDoTable.delete(ID); ### How to: Return all rows from an untyped table The following code shows how to retrieve an entire table. Since you are using a JSON Table, you can selectively retrieve only some of the table's columns. public void showAllUntyped(View view) { new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final JsonElement result = mJsonToDoTable.execute().get(); final JsonArray results = result.getAsJsonArray(); runOnUiThread(new Runnable() { @Override public void run() { mAdapter.clear(); for (JsonElement item : results) { String ID = item.getAsJsonObject().getAsJsonPrimitive("id").getAsString(); String mText = item.getAsJsonObject().getAsJsonPrimitive("text").getAsString(); Boolean mComplete = item.getAsJsonObject().getAsJsonPrimitive("complete").getAsBoolean(); ToDoItem mToDoItem = new ToDoItem(); mToDoItem.setId(ID); mToDoItem.setText(mText); mToDoItem.setComplete(mComplete); mAdapter.add(mToDoItem); } } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } You can do filtering, sorting and paging by concatenating methods that have the same names as those used in the typed programming model. ## How to: Bind data to the user interface Data binding involves three components: - the data source - the screen layout - and the adapter that ties the two together. In our sample code, we return the data from the mobile service table *ToDoItem* into an array. This is one very common pattern for data applications: database queries typically return a collection of rows which the client gets in a list or array. In this sample the array is the data source. The code specifies a screen layout that defines the view of the data that will appear on the device. And the two are bound together with an adapter, which in this code is an extension of the *ArrayAdapter<ToDoItem>* class. ### How to: Define the Layout The layout is defined by several snippets of XML code. Given an existing layout, let's assume the following code represents the **ListView** we want to populate with our server data. In the above code the *listitem* attribute specifies the id of the layout for an individual row in the list. Here is that code, which specifies a check box and its associated text. This gets instantiated once for each item in the list. A more complex layout would specify additional fields in the display. This code is in the *row_list_to_do.xml* file. ### How to: Define the adapter Since the data source of our view is an array of *ToDoItem*, we subclass our adapter from a *ArrayAdapter<ToDoItem>* class. This subclass will produce a View for every *ToDoItem* using the *row_list_to_do* layout. In our code we define the following class which is an extension of the *ArrayAdapter<E>* class: public class ToDoItemAdapter extends ArrayAdapter { You must override the adapter's *getView* method. This sample code is one example of how to do this: details will vary with your application. public View getView(int position, View convertView, ViewGroup parent) { View row = convertView; final ToDoItem currentItem = getItem(position); if (row == null) { LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); row = inflater.inflate(R.layout.row_list_to_do, parent, false); } row.setTag(currentItem); final CheckBox checkBox = (CheckBox) row.findViewById(R.id.checkToDoItem); checkBox.setText(currentItem.getText()); checkBox.setChecked(false); checkBox.setEnabled(true); return row; } We create an instance of this class in our Activity as follows: ToDoItemAdapter mAdapter; mAdapter = new ToDoItemAdapter(this, R.layout.row_list_to_do); Note that the second parameter to the ToDoItemAdapter constructor is a reference to the layout. The call to the constructor is followed by the following code which first gets a reference to the **ListView**, and next calls *setAdapter* to configure itself to use the adapter we just created: ListView listViewToDo = (ListView) findViewById(R.id.listViewToDo); listViewToDo.setAdapter(mAdapter); ### How to: Use the adapter You are now ready to use data binding. The following code shows how to get the items in the mobile service table, clear the apapter, and then call the adapter's *add* method to fill it with the returned items. public void showAll(View view) { new AsyncTask() { @Override protected Void doInBackground(Void... params) { try { final MobileServiceList result = mToDoTable.execute().get(); runOnUiThread(new Runnable() { @Override public void run() { mAdapter.clear(); for (ToDoItem item : result) { mAdapter.add(item); } } }); } catch (Exception exception) { createAndShowDialog(exception, "Error"); } return null; } }.execute(); } You must also call the adapter any time you modify the *ToDoItem* table if you want to display the results of doing that. Since modifications are done on a record by record basis, you will be dealing with a single row instead of a collection. When you insert an item you call the *add* method on the adapter, when deleting, you call the *remove* method. ## How to: Call a custom API A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON. For an example of how to create a custom API in your mobile service, see [How to: define a custom API endpoint](mobile-services-dotnet-backend-define-custom-api.md). ### Update the app to call the custom API 1. We will add a button labelled "Complete All" next to the existing button, and move both buttons down a line. In Android Studio, open the *res\layout\activity_to_do.xml* file in your quickstart project, locate the **LinearLayout** element that contains the **Button** element named `buttonAddToDo`. Copy the **LinearLayout** and paste it immediately following the original one. Delete the **Button** element from the first **LinearLayout**. 2. In the second **LinearLayout**, delete the **EditText** element, and add the following code immediately following the existing **Button** element: 6. In the Windows Phone Store app project, add the following **Button** element in the **ContentPanel**, after the **TextBox** element: 8. Open the shared App.xaml.cs project file and add the following code: protected override void OnActivated(IActivatedEventArgs args) { // Windows Phone 8.1 requires you to handle the respose from the WebAuthenticationBroker. #if WINDOWS_PHONE_APP if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation) { // Completes the sign-in process started by LoginAsync. // Change 'MobileService' to the name of your MobileServiceClient instance. App.MobileService.LoginComplete(args as WebAuthenticationBrokerContinuationEventArgs); } #endif base.OnActivated(args); } If the **OnActivated** method already exists, just add the `#if...#endif` code block. 9. Press the F5 key to run the Windows Store app, click the **Sign in** button, and sign into the app with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query your backend and make updates to data. 10. Right-click the Windows Phone Store app project, click **Set as StartUp Project**, then repeat the previous step to verify that the Windows Phone Store app also runs correctly. >[AZURE.NOTE]If you registered your Windows Store app package information with Mobile Services, you should call the LoginAsync method by supplying a value of **true** for the *useSingleSignOn* parameter. If you do not do this, your users will continue to be presented with a login prompt every time that the login method is called. ## Store the authorization tokens on the client The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the mobile service every time that the app starts. Not only is this method inefficient, you can run into usage-related issues should many customers try to start your app at the same time. A better approach is to cache the authorization token returned by Mobile Services and try to use this first before using a provider-based sign-in. >[AZURE.NOTE]You can cache the token issued by Mobile Services regardless of whether you are using client-managed or service-managed authentication. This tutorial uses service-managed authentication. 1. In the MainPage.xaml.cs project file, add the following **using** statements: using System.Linq; using Windows.Security.Credentials; 2. Replace the **AuthenticateAsync** method with the following code: private async System.Threading.Tasks.Task AuthenticateAsync() { string message; bool success = false; // This sample uses the Facebook provider. var provider = MobileServiceAuthenticationProvider.Facebook; // Use the PasswordVault to securely store and access credentials. PasswordVault vault = new PasswordVault(); PasswordCredential credential = null; try { // Try to get an existing credential from the vault. credential = vault.FindAllByResource(provider.ToString()).FirstOrDefault(); } catch (Exception) { // When there is no matching resource an error occurs, which we ignore. } if (credential != null) { // Create a user from the stored credentials. user = new MobileServiceUser(credential.UserName); credential.RetrievePassword(); user.MobileServiceAuthenticationToken = credential.Password; // Set the user from the stored credentials. App.MobileService.CurrentUser = user; // Consider adding a check to determine if the token is // expired, as shown in this post: http://aka.ms/jww5vp. success = true; message = string.Format("Cached credentials for user - {0}", user.UserId); } else { try { // Login with the identity provider. user = await App.MobileService .LoginAsync(provider); // Create and store the user credentials. credential = new PasswordCredential(provider.ToString(), user.UserId, user.MobileServiceAuthenticationToken); vault.Add(credential); success = true; message = string.Format("You are now logged in - {0}", user.UserId); } catch (MobileServiceInvalidOperationException) { message = "You must log in. Login Required"; } } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); return success; } In this version of **AuthenticateAsync**, the app tries to use credentials stored in the **PasswordVault** to access the service. A regular sign-in is also performed when there is no stored credential. >[AZURE.NOTE]A cached token may be expired, and token expiration can also occur after authentication when the app is in use. To learn how to determine if a token is expired, see [Check for expired authentication tokens](http://aka.ms/jww5vp). For a solution to handling authorization errors related to expiring tokens, see the post [Caching and handling expired tokens in Azure Mobile Services managed SDK](http://blogs.msdn.com/b/carlosfigueira/archive/2014/03/13/caching-and-handling-expired-tokens-in-azure-mobile-services-managed-sdk.aspx). 3. Restart the app twice. Notice that on the first start-up, sign-in with the provider is again required. However, on the second restart the cached credentials are used and sign-in is bypassed. ## Next steps In the next tutorial, [Service-side authorization of Mobile Services users][Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. ## See also + [Enhanced users feature](https://azure.microsoft.com/blog/2014/10/02/custom-login-scopes-single-sign-on-new-asp-net-web-api-updates-to-the-azure-mobile-services-net-backend/)
You can get additional user data maintained by the identity provider in your mobile service by by calling the **ServiceUser.GetIdentitiesAsync()** method in a .NET backend. + [Mobile Services .NET How-to Conceptual Reference]
Learn more about how to use Mobile Services with a .NET client. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Store authentication tokens on the client]: #tokens [Next Steps]:#next-steps [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-dotnet-backend-windows-store-dotnet-get-started.md [Get started with data]: mobile-services-dotnet-backend-windows-store-dotnet-get-started-data.md [Get started with authentication]: mobile-services-dotnet-backend-windows-store-dotnet-get-started-users.md [Get started with push notifications]: mobile-services-dotnet-backend-windows-store-dotnet-get-started-push.md [Authorize users with scripts]: mobile-services-dotnet-backend-service-side-authorization.md [JavaScript and HTML]: mobile-services-dotnet-backend-windows-store-javascript-get-started-users.md [Azure classic portal]: https://manage.windowsazure.com/ [Mobile Services .NET How-to Conceptual Reference]: mobile-services-windows-dotnet-how-to-use-client-library.md [Register your Windows Store app package for Microsoft authentication]: mobile-services-how-to-register-store-app-package-microsoft-authentication.md ================================================ FILE: docs/mobile-services-dotnet-backend-windows-universal-dotnet-upload-data-blob-storage.md ================================================ # Upload images to Azure Storage by using Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-upload-data-blob-storage.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-upload-data-blob-storage.md) - [(Android | Javascript)](mobile-services-android-upload-data-blob-storage.md) ## Overview This topic shows you how to use Azure Mobile Services to enable your app to upload and store user-generated images in Azure Storage. Mobile Services uses a SQL Database to store data. However, binary large object (BLOB) data is more efficiently stored in Azure Blob storage service. You cannot securely distribute with the client app the credentials required to securely upload data to the Blob Storage service. Instead, you must store these credentials in your mobile service and use them to generate a Shared Access Signature (SAS) that is used to upload a new image. The SAS, a credential with a short expiration—in this case 5 minutes, is returned securely by Mobile Services to the client app. The app then uses this temporary credential to upload the image. In this example, downloads from the Blob service are public. In this tutorial you add functionality to the Mobile Services quickstart app to take pictures and upload the images to Azure by using an SAS generated by Mobile Services. ## Prerequisites This tutorial requires the following: + Microsoft Visual Studio 2013 Update 3, or a later version. + [Azure Storage account](https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/) + A camera or other image capture device attached to your computer. This tutorial is based on the Mobile Services quickstart. Before you start this tutorial, you must first complete [Get started with Mobile Services]. ## Install the storage client in the mobile service project To be able to generate an SAS to upload images to Blob storage, you must first add the NuGet package that installs Storage client library in the mobile service project. 1. In **Solution Explorer** in Visual Studio, right-click the mobile service project, and then select **Manage NuGet Packages**. 2. In the left pane, select the **Online** category, select **Stabile Only**, search for **WindowsAzure.Storage**, click **Install** on the **Azure Storage** package, then accept the license agreements. ![](./media/mobile-services-configure-blob-storage/mobile-add-storage-nuget-package-dotnet.png) This adds the client library for Azure storage services to the mobile service project. ## Update the TodoItem definition in the data model The TodoItem class defines the data object, and you need to add the same properties to this class as you did on the client. 1. In Visual Studio 2013, open your mobile service project, expand the DataObjects folder, then open the TodoItem.cs project file. 2. Add the following new properties to the **TodoItem** class: public string containerName { get; set; } public string resourceName { get; set; } public string sasQueryString { get; set; } public string imageUri { get; set; } These properties are used to generate the SAS and to store image information. Note that the casing on these properties matches the JavaScript backend version. >[AZURE.NOTE] When using the default database initializer, Entity Framework will drop and recreate the database when it detects a data model change in the Code First definition. To make this data model change and maintain existing data in the database, you must use Code First Migrations. The default initializer cannot be used against a SQL Database in Azure. For more information, see [How to Use Code First Migrations to Update the Data Model](mobile-services-dotnet-backend-how-to-use-code-first-migrations.md). ## Update the TodoItem controller to generate a shared access signature The existing **TodoItemController** is updated so that the **PostTodoItem** method generates an SAS when a new TodoItem is inserted. You also 0. If you haven't yet created your storage account, see [How To Create a Storage Account]. 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Storage**, click the storage account, then click **Manage Keys**. 2. Make a note of the **Storage Account Name** and **Access Key**. 3. In your mobile service, click the **Configure** tab, scroll down to **App settings** and enter a **Name** and **Value** pair for each of the following that you obtained from the storage account, then click **Save**. + `STORAGE_ACCOUNT_NAME` + `STORAGE_ACCOUNT_ACCESS_KEY` ![](./media/mobile-services-configure-blob-storage/mobile-blob-storage-app-settings.png) The storage account access key is stored encrypted in app settings. You can access this key from any server script at runtime. For more information, see [App settings]. 4. In Solution Explorer in Visual Studio, open the Web.config file for the mobile service project and add the following new app settings, replacing the placeholders with the storage account name and access key that you just set in the portal: The mobile service uses these stored settings when it runs on the local computer, which lets you test the code before you publish it. When running in Azure, the mobile service instead uses app settings values set in the portal and ignores these project settings. 7. In the Controllers folder, open the TodoItemController.cs file and add the following **using** directives: using System; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; 8. Replace the existing **PostTodoItem** method with the following code: public async Task PostTodoItem(TodoItem item) { string storageAccountName; string storageAccountKey; // Try to get the Azure storage account token from app settings. if (!(Services.Settings.TryGetValue("STORAGE_ACCOUNT_NAME", out storageAccountName) | Services.Settings.TryGetValue("STORAGE_ACCOUNT_ACCESS_KEY", out storageAccountKey))) { Services.Log.Error("Could not retrieve storage account settings."); } // Set the URI for the Blob Storage service. Uri blobEndpoint = new Uri(string.Format("https://{0}.blob.core.windows.net", storageAccountName)); // Create the BLOB service client. CloudBlobClient blobClient = new CloudBlobClient(blobEndpoint, new StorageCredentials(storageAccountName, storageAccountKey)); if (item.containerName != null) { // Set the BLOB store container name on the item, which must be lowercase. item.containerName = item.containerName.ToLower(); // Create a container, if it doesn't already exist. CloudBlobContainer container = blobClient.GetContainerReference(item.containerName); await container.CreateIfNotExistsAsync(); // Create a shared access permission policy. BlobContainerPermissions containerPermissions = new BlobContainerPermissions(); // Enable anonymous read access to BLOBs. containerPermissions.PublicAccess = BlobContainerPublicAccessType.Blob; container.SetPermissions(containerPermissions); // Define a policy that gives write access to the container for 5 minutes. SharedAccessBlobPolicy sasPolicy = new SharedAccessBlobPolicy() { SharedAccessStartTime = DateTime.UtcNow, SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5), Permissions = SharedAccessBlobPermissions.Write }; // Get the SAS as a string. item.sasQueryString = container.GetSharedAccessSignature(sasPolicy); // Set the URL used to store the image. item.imageUri = string.Format("{0}{1}/{2}", blobEndpoint.ToString(), item.containerName, item.resourceName); } // Complete the insert operation. TodoItem current = await InsertAsync(item); return CreatedAtRoute("Tables", new { id = current.Id }, current); } This POST method now generates a new SAS for the inserted item, which is valid for 5 minutes, and assigns the value of the generated SAS to the `sasQueryString` property of the returned item. The `imageUri` property is also set to the resource path of the new BLOB to enable image display during binding in the client UI. >[AZURE.NOTE] This code creates an SAS for an individual BLOB. If you need to upload multiple blobs to a container using the same SAS, you can instead call the generateSharedAccessSignature method with an empty blob resource name, like this:
blobService.generateSharedAccessSignature(containerName, '', sharedAccessPolicy);
Next, you will update the quickstart app to add image upload functionality by using the SAS generated on insert. [How To Create a Storage Account]: https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/ [App settings]: http://msdn.microsoft.com/library/windowsazure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7 ## Install the Storage client for Windows Store apps To be able to use an SAS to upload images to Blob storage, you must first add the NuGet package that installs Storage client library for Windows Store apps. 1. In **Solution Explorer** in Visual Studio, right-click the project name, and then select **Manage NuGet Packages**. 2. In the left pane, select the **Online** category, search for `WindowsAzure.Storage`, click **Install** on the **Azure Storage** package, then accept the license agreements. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-add-storage-nuget-package-dotnet.png) This adds the client library for Azure storage services to the project. Next, you will update the quickstart app to capture and upload images. ## Update the quickstart client app to capture and upload images 1. In Visual Studio, open the Package.appxmanifest file for the Windows app project and in the **Capabilities** tab enable the **Webcam** and **Microphone** capabilities. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-app-manifest-camera.png) This makes sure that your app can use a camera attached to the computer. Users will be requested to allow camera access the first time that the app is run. 2. Repeat the step above for the Windows Phone app project. 3. In the Windows app project, open the MainPage.xaml file and replace the **StackPanel** element directly after the first **QuickStartTask** element with the following code: 2. Replace the **StackPanel** element in the **DataTemplate** with the following code: This adds an image to the **ItemTemplate** and sets its binding source as the URI of the uploaded image in the Blob Storage service. 3. In the Windows Phone app project, open the MainPage.xaml file and replace the **ButtonSave** element with the following code: 2. Replace the **StackPanel** element in the **DataTemplate** with the following code: 4. In the shared DataModel folder, open the TodoItem.cs project file and add add the following properties to the TodoItem class: [JsonProperty(PropertyName = "containerName")] public string ContainerName { get; set; } [JsonProperty(PropertyName = "resourceName")] public string ResourceName { get; set; } [JsonProperty(PropertyName = "sasQueryString")] public string SasQueryString { get; set; } [JsonProperty(PropertyName = "imageUri")] public string ImageUri { get; set; } 3. Open the shared MainPage.cs project file and add the following **using** statements: using Windows.Media.Capture; using Windows.Media.MediaProperties; using Windows.Storage; using Windows.UI.Xaml.Input; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; using Windows.UI.Xaml.Media.Imaging; 5. Add the following code to the MainPage class: // Use a StorageFile to hold the captured image for upload. StorageFile media = null; MediaCapture cameraCapture; bool IsCaptureInProgress; private async Task CaptureImage() { // Capture a new photo or video from the device. cameraCapture = new MediaCapture(); cameraCapture.Failed += cameraCapture_Failed; // Initialize the camera for capture. await cameraCapture.InitializeAsync(); #if WINDOWS_PHONE_APP cameraCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees); cameraCapture.SetRecordRotation(VideoRotation.Clockwise90Degrees); #endif captureGrid.Visibility = Windows.UI.Xaml.Visibility.Visible; previewElement.Visibility = Windows.UI.Xaml.Visibility.Visible; previewElement.Source = cameraCapture; await cameraCapture.StartPreviewAsync(); } private async void previewElement_Tapped(object sender, TappedRoutedEventArgs e) { // Block multiple taps. if (!IsCaptureInProgress) { IsCaptureInProgress = true; // Create the temporary storage file. media = await ApplicationData.Current.LocalFolder .CreateFileAsync("capture_file.jpg", CreationCollisionOption.ReplaceExisting); // Take the picture and store it locally as a JPEG. await cameraCapture.CapturePhotoToStorageFileAsync( ImageEncodingProperties.CreateJpeg(), media); captureButtons.Visibility = Visibility.Visible; // Use the stored image as the preview source. BitmapImage tempBitmap = new BitmapImage(new Uri(media.Path)); imagePreview.Source = tempBitmap; imagePreview.Visibility = Visibility.Visible; previewElement.Visibility = Visibility.Collapsed; IsCaptureInProgress = false; } } private async void cameraCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs) { // It's safest to return this back onto the UI thread to show the message dialog. MessageDialog dialog = new MessageDialog(errorEventArgs.Message); await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await dialog.ShowAsync(); }); } private async void ButtonCapture_Click(object sender, RoutedEventArgs e) { // Prevent multiple initializations. ButtonCapture.IsEnabled = false; await CaptureImage(); } private async void ButtonRetake_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { // Reset the capture and then start again. await ResetCaptureAsync(); await CaptureImage(); } private async void ButtonCancel_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { await ResetCaptureAsync(); } private async Task ResetCaptureAsync() { captureGrid.Visibility = Windows.UI.Xaml.Visibility.Collapsed; imagePreview.Visibility = Visibility.Collapsed; captureButtons.Visibility = Visibility.Collapsed; previewElement.Source = null; ButtonCapture.IsEnabled = true; // Make sure we stop the preview and release resources. await cameraCapture.StopPreviewAsync(); cameraCapture.Dispose(); media = null; } This code displays the UI used to capture an image, and saves the image to a storage file. 6. Replace the existing `InsertTodoItem` method with the following code: private async Task InsertTodoItem(TodoItem todoItem) { string errorString = string.Empty; if (media != null) { // Set blob properties of TodoItem. todoItem.ContainerName = "todoitemimages"; // Use a unigue GUID to avoid collisions. todoItem.ResourceName = Guid.NewGuid().ToString(); } // Send the item to be inserted. When blob properties are set this // generates an SAS in the response. await todoTable.InsertAsync(todoItem); // If we have a returned SAS, then upload the blob. if (!string.IsNullOrEmpty(todoItem.SasQueryString)) { // Get the URI generated that contains the SAS // and extract the storage credentials. StorageCredentials cred = new StorageCredentials(todoItem.SasQueryString); var imageUri = new Uri(todoItem.ImageUri); // Instantiate a Blob store container based on the info in the returned item. CloudBlobContainer container = new CloudBlobContainer( new Uri(string.Format("https://{0}/{1}", imageUri.Host, todoItem.ContainerName)), cred); // Get the new image as a stream. using (var inputStream = await media.OpenReadAsync()) { // Upload the new image as a BLOB from the stream. CloudBlockBlob blobFromSASCredential = container.GetBlockBlobReference(todoItem.ResourceName); await blobFromSASCredential.UploadFromStreamAsync(inputStream); } // When you request an SAS at the container-level instead of the blob-level, // you are able to upload multiple streams using the same container credentials. await ResetCaptureAsync(); } // Add the new item to the collection. items.Add(todoItem); } This code sends a request to the mobile service to insert a new TodoItem. The response contains the SAS, which is then used to upload the image from local storage to Azure Blob storage. The URL of the uploaded image is used in data binding. The final step is to test both versions of the app and validate that uploads succeed from both devices. ## Test uploading the images in your app 1. In Visual Studio, make sure that the Windows project is set as the default project, then press the F5 key to run the app. 2. Enter text in the textbox under **Insert a TodoItem**, then click **Photo**. 3. Click or tap the preview to take a picture, then click **Upload** to insert the new item and upload the image. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-quickstart-blob-appbar2.png) 4. The new item, along with the uploaded image, is displayed in the list view. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-quickstart-blob-ie.png) >[AZURE.NOTE]The image is downloaded automatically from Blob storage when the *imageUri* property of the new item is bound to the **Image** control. 5. Stop the app and restart the Windows Phone project version of the app. The previously uploaded image is displayed. 6. As before, enter some text in the textbox, then click **Photo**. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-upload-blob-app-view-wp8.png) 3. Tap the preview to take a picture, then click **Upload** to insert the new item and upload the image. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-upload-blob-app-view-final-wp8.png) You have completed the upload images tutorial. ## Next steps Now that you have been able to securely upload images by integrating your mobile service with the Blob service, check out some of the other backend service and integration topics: + [Schedule backend jobs in Mobile Services](mobile-services-dotnet-backend-schedule-recurring-tasks.md) Learn how to use the Mobile Services job scheduler functionality to define server script code that is executed on a schedule that you define. + [Mobile Services .NET How-to Conceptual Reference](mobile-services-dotnet-how-to-use-client-library.md) Learn more about how to use Mobile Services with .NET [Install the Storage Client library]: #install-storage-client [Update the client app to capture images]: #add-select-images [Install the storage client in the mobile service project]: #storage-client-server [Update the TodoItem definition in the data model]: #update-data-model [Update the table controller to generate an SAS]: #update-scripts [Upload images to test the app]: #test [Next Steps]:#next-steps [Get started with Mobile Services]: mobile-services-windows-store-dotnet-get-started.md [How To Create a Storage Account]: https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/ [Azure Storage Client library for Store apps]: http://go.microsoft.com/fwlink/p/?LinkId=276866 ================================================ FILE: docs/mobile-services-dotnet-backend-xamarin-android-get-started-push.md ================================================ # Add push notifications to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Overview This topic shows you how to use Azure Mobile Services to send push notifications to a Xamarin.Android app. In this tutorial you add push notifications using the Google Cloud Messaging (GCM) service to the [Get started with Mobile Services] project. When complete, your mobile service will send a push notification each time a record is inserted. This tutorial requires the following: + An active Google account. + [Google Cloud Messaging Client Component]. You will add this component during the tutorial. You should already have the Xamarin.Android and [Azure Mobile Services][Azure Mobile Services Component] components installed in your project from when you completed [Get started with Mobile Services]. ## Enable Google Cloud Messaging 1. Navigate to the [Google Cloud Console](https://console.developers.google.com/project), sign in with your Google account credentials. 2. Click **Create Project**, type a project name, then click **Create**. If requested, carry out the SMS Verification, and click **Create** again. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-new-project.png) Type in your new **Project name** and click **Create project**. 3. Click the **Utilities and More** button and then click **Project Information**. Make a note of the **Project Number**. You will need to set this value as the `SenderId` variable in the client app. ![](./media/mobile-services-enable-google-cloud-messaging/notification-hubs-utilities-and-more.png) 4. In the project dashboard, under **Mobile APIs**, click **Google Cloud Messaging**, then on the next page click **Enable API** and accept the terms of service. ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-GCM.png) ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-gcm-2.png) 5. In the project dashboard, Click **Credentials** > **Create Credential** > **API Key**. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-create-server-key.png) 6. In **Create a new key**, click **Server key**, type a name for your key, then click **Create**. 7. Make a note of the **API KEY** value. You will use this API key value to enable Azure to authenticate with GCM and send push notifications on behalf of your app. ## Configure your mobile service to send push requests 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your app. 2. Click the **Push** tab, enter the **API Key** value obtained from GCM in the previous procedure, then click **Save**. ![](./media/mobile-services-android-configure-push/mobile-push-tab-android.png) >[AZURE.NOTE]When you set your GCM credentials for enhanced push notifications in the Push tab in the portal, they are shared with Notification Hubs to configure the notification hub with your app. Both your mobile service and your app are now configured to work with GCM and Notification Hubs. ## Update the mobile service to send push notifications 1. In Visual Studio Solution Explorer, expand the **Controllers** folder in the mobile service project. Open TodoItemController.cs. At the top of the file, add the following `using` statements: using System; using System.Collections.Generic; 2. Update the `PostTodoItem` method definition with the following code: public async Task PostTodoItem(TodoItem item) { TodoItem current = await InsertAsync(item); Dictionary data = new Dictionary() { { "message", item.Text} }; GooglePushMessage message = new GooglePushMessage(data, TimeSpan.FromHours(1)); try { var result = await Services.Push.SendAsync(message); Services.Log.Info(result.State.ToString()); } catch (System.Exception ex) { Services.Log.Error(ex.Message, null, "Push.SendAsync Error"); } return CreatedAtRoute("Tables", new { id = current.Id }, current); } This code will send a push notification (with the text of the inserted item) after inserting a todo item. In the event of an error, the code will add an error log entry which is viewable on the **Logs** tab of the mobile service in the [Azure classic portal](https://manage.windowsazure.com/). 3. Republish your mobile service project to Azure. ## Configure the existing project for push notifications 1. In the Solution view (or **Solution Explorer** in Visual Studio), right-click the **Components** folder, click **Get More Components...**, search for the **Google Cloud Messaging Client** component and add it to the project. 2. Open the ToDoActivity.cs project file and add the following using statement to the class: using Gcm.Client; 3. In the **ToDoActivity** class, add the following new code: // Create a new instance field for this activity. static ToDoActivity instance = new ToDoActivity(); // Return the current activity instance. public static ToDoActivity CurrentActivity { get { return instance; } } // Return the Mobile Services client. public MobileServiceClient CurrentClient { get { return client; } } This enables you to access the mobile client instance from the push handler service process. 4. Add the following code to the **OnCreate** method, after the **MobileServiceClient** is created: // Set the current instance of TodoActivity. instance = this; // Make sure the GCM client is set up correctly. GcmClient.CheckDevice(this); GcmClient.CheckManifest(this); // Register the app for push notifications. GcmClient.Register(this, ToDoBroadcastReceiver.senderIDs); Your **ToDoActivity** is now prepared for adding push notifications. ## Add push notifications code to your app 4. Create a new class in the project called `ToDoBroadcastReceiver`. 5. Add the following using statements to **ToDoBroadcastReceiver** class: using Gcm.Client; using Microsoft.WindowsAzure.MobileServices; 6. Add the following permission requests between the **using** statements and the **namespace** declaration: [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] //GET_ACCOUNTS is only needed for android versions 4.0.3 and below [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")] 7. Replace the existing **ToDoBroadcastReceiver** class definition with the following: [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })] public class ToDoBroadcastReceiver : GcmBroadcastReceiverBase { // Set the Google app ID. public static string[] senderIDs = new string[] { "" }; } In the above code, you must replace _``_ with the project number assigned by Google when you provisioned your app in the Google developer portal. 8. In the ToDoBroadcastReceiver.cs project file, add the following code that defines the **PushHandlerService** class: // The ServiceAttribute must be applied to the class. [Service] public class PushHandlerService : GcmServiceBase { public static string RegistrationID { get; private set; } public PushHandlerService() : base(ToDoBroadcastReceiver.senderIDs) { } } Note that this class derives from **GcmServiceBase** and that the **Service** attribute must be applied to this class. >[AZURE.NOTE]The **GcmServiceBase** class implements the **OnRegistered()**, **OnUnRegistered()**, **OnMessage()** and **OnError()** methods. You must override these methods in the **PushHandlerService** class. 5. Add the following code to the **PushHandlerService** class that overrides the **OnRegistered** event handler. protected override void OnRegistered(Context context, string registrationId) { System.Diagnostics.Debug.WriteLine("The device has been registered with GCM.", "Success!"); // Get the MobileServiceClient from the current activity instance. MobileServiceClient client = ToDoActivity.CurrentActivity.CurrentClient; var push = client.GetPush(); List tags = null; //// (Optional) Uncomment to add tags to the registration. //var tags = new List() { "myTag" }; // create tags if you want try { // Make sure we run the registration on the same thread as the activity, // to avoid threading errors. ToDoActivity.CurrentActivity.RunOnUiThread( async () => await push.RegisterNativeAsync(registrationId, tags)); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine( string.Format("Error with Azure push registration: {0}", ex.Message)); } } This method uses the returned GCM registration ID to register with Azure for push notifications. 10. Override the **OnMessage** method in **PushHandlerService** with the following code: protected override void OnMessage(Context context, Intent intent) { string message = string.Empty; // Extract the push notification message from the intent. if (intent.Extras.ContainsKey("message")) { message = intent.Extras.Get("message").ToString(); var title = "New item added:"; // Create a notification manager to send the notification. var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager; // Create a new intent to show the notification in the UI. PendingIntent contentIntent = PendingIntent.GetActivity(context, 0, new Intent(this, typeof(ToDoActivity)), 0); // Create the notification using the builder. var builder = new Notification.Builder(context); builder.SetAutoCancel(true); builder.SetContentTitle(title); builder.SetContentText(message); builder.SetSmallIcon(Resource.Drawable.ic_launcher); builder.SetContentIntent(contentIntent); var notification = builder.Build(); // Display the notification in the Notifications Area. notificationManager.Notify(1, notification); } } 12. Override the **OnUnRegistered()** and **OnError()** methods with the following code. protected override void OnUnRegistered(Context context, string registrationId) { throw new NotImplementedException(); } protected override void OnError(Context context, string errorId) { System.Diagnostics.Debug.WriteLine( string.Format("Error occurred in the notification: {0}.", errorId)); } ## Test the app against the published mobile service You can test the app by directly attaching an Android phone with a USB cable, or by using a virtual device in the emulator. ### Enable push notifications for local testing You can optionally test push notifications with your mobile service running on the local computer or VM before you publish to Azure. To do this, you must set information about the notification hub used by your app in the web.config file. This information is only used when running locally to connect to the notification hub; it is ignored when published to Azure. 1. Back in the **Push** tab of your mobile service, click the **Notification Hub** link. ![](./media/mobile-services-dotnet-backend-configure-local-push/link-to-notification-hub.png) This navigates to the notification hub used by your mobile service. 2. In the notification hub page, make a note of the name of your notification hub, then click **View Connection String**. ![](./media/mobile-services-dotnet-backend-configure-local-push/notification-hub-page.png) 3. In the **Access connection information**, copy the **DefaultFullSharedAccessSignature** connection string. ![](./media/mobile-services-dotnet-backend-configure-local-push/notification-hub-connection-string.png) 4. In your mobile service project in Visual Studio, open the Web.config file for the service and in **connectionStrings**, replace the connection string for **MS_NotificationHubConnectionString** with the connection string from the previous step. 5. In **appSettings**, replace the value of the **MS_NotificationHubName** app setting with the name of the notification hub. Now, the mobile service project is configured to connect to the notification hub in Azure when running locally. Note that it is important to use the same notification hub name and connection string as the portal because these Web.config project settings are overridden by the portal settings when running in Azure. 4. Create a new class in the project called `ToDoBroadcastReceiver`. 5. Add the following using statements to **ToDoBroadcastReceiver** class: using Gcm.Client; using Microsoft.WindowsAzure.MobileServices; 6. Add the following permission requests between the **using** statements and the **namespace** declaration: [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] //GET_ACCOUNTS is only needed for android versions 4.0.3 and below [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")] 7. Replace the existing **ToDoBroadcastReceiver** class definition with the following: [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })] public class ToDoBroadcastReceiver : GcmBroadcastReceiverBase { // Set the Google app ID. public static string[] senderIDs = new string[] { "" }; } In the above code, you must replace _``_ with the project number assigned by Google when you provisioned your app in the Google developer portal. 8. In the ToDoBroadcastReceiver.cs project file, add the following code that defines the **PushHandlerService** class: // The ServiceAttribute must be applied to the class. [Service] public class PushHandlerService : GcmServiceBase { public static string RegistrationID { get; private set; } ### Setting up the Android emulator for testing When you run this app in the emulator, make sure that you use an Android Virtual Device (AVD) that supports Google APIs. > [AZURE.IMPORTANT] In order to receive push notifications, you must set up a Google account on your Android Virtual Device (in the emulator, navigate to **Settings** and click **Add Account**). Also, make sure that the emulator is connected to the Internet. 1. From **Tools**, click **Open Android Emulator Manager**, select your device, and then click **Edit**. ![Android Virtual Device Manager](./media/mobile-services-android-push-notifications-test/notification-hub-create-android-app7.png) 2. Select **Google APIs** in **Target**, then click **OK**. ![Edit the Android Virtual Device](./media/mobile-services-android-push-notifications-test/notification-hub-create-android-app8.png) 3. On the top toolbar, click **Run**, and then select your app. This starts the emulator and runs the app. The app retrieves the *registrationId* from GCM and registers with the Notification Hub. ### Inserting a new item generates a notification. 1. In the app, type meaningful text, such as _A new Mobile Services task_ and then click the **Add** button. 2. Swipe down from the top of the screen to open the device's Notification Center to see the notification. ![View notification in the Notification Center](./media/mobile-services-android-push-notifications-test/notification-area-received.png) [Get started with Mobile Services]: mobile-services-dotnet-backend-xamarin-android-get-started.md [Google Cloud Messaging Client Component]: http://components.xamarin.com/view/GCMClient/ [Azure Mobile Services Component]: http://components.xamarin.com/view/azure-mobile-services/ ================================================ FILE: docs/mobile-services-dotnet-backend-xamarin-android-get-started-users.md ================================================ # Get started with authentication in Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to authenticate users in Azure Mobile Services from your app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial walks you through these basic steps to enable authentication in your app: 1. [Register your app for authentication and configure Mobile Services] 2. [Restrict table permissions to authenticated users] 3. [Add authentication to the app] This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ### (Optional) Configure your .NET Mobile Service for Azure Active Directory >[AZURE.NOTE] These steps are optional because they only apply to the Azure Active Directory login provider. 1. Install the [WindowsAzure.MobileServices.Backend.Security NuGet package](https://www.nuget.org/packages/WindowsAzure.MobileServices.Backend.Security). 2. In Visual Studio expand App_Start and open WebApiConfig.cs. Add the following `using` statement at the top: using Microsoft.WindowsAzure.Mobile.Service.Security.Providers; 3. Also in WebApiConfig.cs, add the following code to the `Register` method, immediately after `options` is instantiated: options.LoginProviders.Remove(typeof(AzureActiveDirectoryLoginProvider)); options.LoginProviders.Add(typeof(AzureActiveDirectoryExtendedLoginProvider)); ## Restrict permissions to authenticated users By default, all requests to mobile service resources are restricted to clients that present the application key, which does not strictly secure access to resources. To secure your resources, you must restrict access to only authenticated clients. 1. In Visual Studio, open your mobile service project, expand the Controllers folder, and open **TodoItemController.cs**. The **TodoItemController** class implements data access for the TodoItem table. Add the following `using` statement: using Microsoft.WindowsAzure.Mobile.Service.Security; 2. Apply the following _AuthorizeLevel_ attribute to the **TodoItemController** class. [AuthorizeLevel(AuthorizationLevel.User)] This makes sure that all operations against the _TodoItem_ table require an authenticated user. You can also apply the *AuthorizeLevel* attribute at the method level. 3. (Optional) If you wish to debug authentication locally, expand the `App_Start` folder, open **WebApiConfig.cs**, and add the following code to the **Register** method. config.SetIsHosted(true); This tells the local mobile service project to run as if it is being hosted in Azure, including honoring the *AuthorizeLevel* settings. Without this setting, all HTTP requests to localhost are permitted without authentication despite the *AuthorizeLevel* setting. When you enable self-hosted mode, you also need to set a value for the local application key. 4. (Optional) In the web.config project file, set a string value for the `MS_ApplicationKey` app setting. This is the password that you use (with no username) to test the API help pages when you run the service locally. This string value is not used by the live site in Azure, and you do not need to use the actual application key; any valid string value will work. 4. Republish your project.
  1. In Visual Studio or Xamarin Studio, run the client project on a device or simulator. Verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts.

    This happens because the app attempts to access Mobile Services as an unauthenticated user, but the TodoItem table now requires authentication.

Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app 1. Add the following property to the **TodoActivity** class: private MobileServiceUser user; 2. Add the following method to the **TodoActivity** class: private async Task Authenticate() { try { user = await client.LoginAsync(this, MobileServiceAuthenticationProvider.Facebook); CreateAndShowDialog(string.Format("you are now logged in - {0}", user.UserId), "Logged in!"); } catch (Exception ex) { CreateAndShowDialog(ex, "Authentication failed"); } } This creates a new method to handle the authentication process. The user is authenticated by using a Facebook login. A dialog is displayed which displays the ID of the authenticated user. > [AZURE.NOTE] If you are using an identity provider other than a Facebook, change the value passed to **LoginAsync** above to one of the following: _MicrosoftAccount_, _Twitter_, _Google_, or _WindowsAzureActiveDirectory_. 3. In the **OnCreate** method, add the following line of code after the code that instantiates the `MobileServiceClient` object. await Authenticate(); // add this line This call starts the authentication process and awaits it asynchronously. 4. From the **Run** menu, click **Start debugging** to start the app and sign in with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query Mobile Services and make updates to data. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-dotnet-backend-xamarin-android-get-started.md [Get started with authentication]: mobile-services-dotnet-backend-xamarin-android-get-started-users.md [Get started with push notifications]: mobile-services-dotnet-backend-xamarin-android-get-started-push.md [Authorize users with scripts]: mobile-services-dotnet-backend-service-side-authorization.md [JavaScript and HTML]: mobile-services-dotnet-backend-windows-store-javascript-get-started-users.md ================================================ FILE: docs/mobile-services-dotnet-backend-xamarin-android-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a Xamarin Android app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple _To do list_ app that stores app data in the new mobile service. The mobile service that you will create uses the supported .NET languages using Visual Studio for server-side business logic and to manage the mobile service. To create a mobile service that lets you write your server-side business logic in JavaScript, see the [JavaScript backend version] of this topic. >[AZURE.NOTE]This topic shows you how to create a new mobile service project by using the Azure classic portal. By using Visual Studio 2013 Update 2, you can also add a new mobile service project to an existing Visual Studio solution. For more information, see [Quickstart: Add a mobile service (.NET backend)](http://msdn.microsoft.com/library/windows/apps/dn629482.aspx) A screenshot from the completed app is below: ![][0] Completing this tutorial is a prerequisite for all other Mobile Services tutorials for Xamarin Android apps. >[AZURE.NOTE]To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A0E0E5C02&returnurl=http%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fdocumentation%2Farticles%2Fmobile-services-dotnet-backend-xamarin-android-get-started). >This tutorial requires [Visual Studio Professional 2013](https://go.microsoft.com/fwLink/p/?LinkID=257546). A free trial version is available. ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** page, select **Create a free 20 MB SQL Database**, select **.NET** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. > [AZURE.NOTE] As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Xamarin Android app Once you have created your mobile service, you can follow an easy quickstart in the classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will download a new Xamarin Android app and a service project for your mobile service. 1. If you haven't already done so, install Visual Studio with Xamarin. Instructions can be found on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). You can also use Xamarin Studio on a Mac OS X machine, see [Setup, install, and verifications for Mac users](https://msdn.microsoft.com/library/mt488770.aspx). 2. In the [classic portal], click **Mobile Services**, and then click the mobile service that you just created. 3. In the quickstart tab, click **Xamarin** under **Choose platform** and expand **Create a new Xamarin app**. ![][6] This displays the three easy steps to create a Xamarin Android app connected to your mobile service. ![][7] 4. Under **Download and publish your service to the cloud**, select **Android** and click **Download**. This downloads a solution containing projects for both the mobile service and for the sample _To do list_ application that is connected to your mobile service. Save the compressed project file to your local computer, and make a note of where you save it. 5. Download your publish profile, save the downloaded file to your local computer, and make a note of where you save it. ## Test the mobile service The mobile service project lets you run your new mobile service locally. This makes it easy to debug your service code before you even publish it to Azure. 1. On your Windows PC, download your personalized server project, extract it, and then open it in Visual Studio. 2. Press the **F5** key to rebuild the project and start the mobile service locally. A web page is displayed after the mobile service starts successfully. ## Publish your mobile service 1. In Visual Studio, right-click the project, click **Publish** > **Microsoft Azure Mobile Services**. Instead of using Visual Studio, [you may also use Git](./ mobile-services-dotnet-backend-store-code-source-control.md). 2. Sign in with Azure credentials and select your service from **Existing Mobile Services**. Visual Studio downloads your publish settings directly from Azure. Finally, click **Publish**. ## Run the Xamarin Android app The final stage of this tutorial is to build and run your new app. 1. Navigate to the client project within the mobile service solution, in either Visual Studio or Xamarin Studio. 2. Press the **Run** button to build the project and start the app. You will be asked to select an emulator or a connected USB device. > [AZURE.NOTE] To be able to run the project in the Android emulator, you must define a least one Android Virtual Device (AVD). Use the AVD Manager to create and manage these devices. 3. In the app, type meaningful text, such as _Complete the tutorial_ and then click the plus (**+**) icon. ![][10] This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. > [AZURE.NOTE] > You can review the code that accesses your mobile service to query and insert data, which is found in the ToDoActivity.cs C# file. ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync]
Learn how the quickstart uses offline data sync to make the app responsive and robust. * [Get started with authentication]
Learn how to authenticate users of your app with an identity provider. * [Get started with push notifications]
Learn how to send a very basic push notification to your app. * [Troubleshoot a Mobile Services .NET backend]
Learn how to diagnose and fix issues that can arise with a Mobile Services .NET backend. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Next Steps]:#next-steps [0]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-quickstart-completed-android.png [6]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-portal-quickstart-xamarin.png [7]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-quickstart-steps-xamarin-android.png [8]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-xamarin-project-android-vs.png [9]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-xamarin-project-android-xs.png [10]: ./media/mobile-services-dotnet-backend-xamarin-android-get-started/mobile-quickstart-startup-android.png [Get started with offline data sync]: mobile-services-xamarin-android-get-started-offline-data.md [Get started with authentication]: mobile-services-dotnet-backend-xamarin-android-get-started-users.md [Get started with push notifications]: mobile-services-dotnet-backend-xamarin-android-get-started-push.md [Visual Studio Professional 2013]: https://go.microsoft.com/fwLink/p/?LinkID=257546 [Mobile Services SDK]: http://go.microsoft.com/fwlink/?LinkId=257545 [JavaScript and HTML]: mobile-services-win8-javascript/ [Azure classic portal]: https://manage.windowsazure.com/ [classic portal]: https://manage.windowsazure.com/ [JavaScript backend version]: mobile-services-android-get-started.md [Troubleshoot a Mobile Services .NET backend]: mobile-services-dotnet-backend-how-to-troubleshoot.md ================================================ FILE: docs/mobile-services-dotnet-backend-xamarin-ios-get-started-users.md ================================================ # Add authentication to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to authenticate users in Mobile Services from your app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial walks you through these basic steps to enable authentication in your app: 1. [Register your app for authentication and configure Mobile Services] 2. [Restrict table permissions to authenticated users] 3. [Add authentication to the app] This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ### (Optional) Configure your .NET Mobile Service for Azure Active Directory >[AZURE.NOTE] These steps are optional because they only apply to the Azure Active Directory login provider. 1. Install the [WindowsAzure.MobileServices.Backend.Security NuGet package](https://www.nuget.org/packages/WindowsAzure.MobileServices.Backend.Security). 2. In Visual Studio expand App_Start and open WebApiConfig.cs. Add the following `using` statement at the top: using Microsoft.WindowsAzure.Mobile.Service.Security.Providers; 3. Also in WebApiConfig.cs, add the following code to the `Register` method, immediately after `options` is instantiated: options.LoginProviders.Remove(typeof(AzureActiveDirectoryLoginProvider)); options.LoginProviders.Add(typeof(AzureActiveDirectoryExtendedLoginProvider)); ## Restrict permissions to authenticated users By default, all requests to mobile service resources are restricted to clients that present the application key, which does not strictly secure access to resources. To secure your resources, you must restrict access to only authenticated clients. 1. In Visual Studio, open your mobile service project, expand the Controllers folder, and open **TodoItemController.cs**. The **TodoItemController** class implements data access for the TodoItem table. Add the following `using` statement: using Microsoft.WindowsAzure.Mobile.Service.Security; 2. Apply the following _AuthorizeLevel_ attribute to the **TodoItemController** class. [AuthorizeLevel(AuthorizationLevel.User)] This makes sure that all operations against the _TodoItem_ table require an authenticated user. You can also apply the *AuthorizeLevel* attribute at the method level. 3. (Optional) If you wish to debug authentication locally, expand the `App_Start` folder, open **WebApiConfig.cs**, and add the following code to the **Register** method. config.SetIsHosted(true); This tells the local mobile service project to run as if it is being hosted in Azure, including honoring the *AuthorizeLevel* settings. Without this setting, all HTTP requests to localhost are permitted without authentication despite the *AuthorizeLevel* setting. When you enable self-hosted mode, you also need to set a value for the local application key. 4. (Optional) In the web.config project file, set a string value for the `MS_ApplicationKey` app setting. This is the password that you use (with no username) to test the API help pages when you run the service locally. This string value is not used by the live site in Azure, and you do not need to use the actual application key; any valid string value will work. 4. Republish your project.    6. In Visual Studio or Xamarin Studio, run the client project on a device or simulator. Verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts.      This happens because the app attempts to access Mobile Services as an unauthenticated user, but the *TodoItem* table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app In this section, you will modify the app to display a login screen before displaying data. When the app starts, it will not connect to your mobile service and will not display any data. After the first time that the user performs the refresh gesture, the login screen will appear; after successful login the list of todo items will be displayed. 1. In the client project, open the file **QSTodoService.cs** and add the following declarations to QSTodoService: // Mobile Service logged in user private MobileServiceUser user; public MobileServiceUser User { get { return user; } } 2. Add a new method **Authenticate** to **QSTodoService** with the following definition: private async Task Authenticate(UIViewController view) { try { user = await client.LoginAsync(view, MobileServiceAuthenticationProvider.Facebook); } catch (Exception ex) { Console.Error.WriteLine (@"ERROR - AUTHENTICATION FAILED {0}", ex.Message); } } > [AZURE.NOTE] When you use an identity provider other than a Facebook, change the value passed to **LoginAsync** above to one of the following: _MicrosoftAccount_, _Twitter_, _Google_, or _WindowsAzureActiveDirectory_. 3. Open **QSTodoListViewController.cs** and modify the method definition of **ViewDidLoad** to remove or comment-out the call to **RefreshAsync()** near the end. 4. Add the following code at the top of the **RefreshAsync** method definition: // Add at the start of the RefreshAsync method. if (todoService.User == null) { await QSTodoService.DefaultService.Authenticate (this); if (todoService.User == null) { Console.WriteLine ("You must sign in."); return; } } This displays a sign-in screen to attempt authentication when the **User** property is null. When the login is successful the **User** is set. 5. Press the **Run** button to build the project and start the app in the iPhone simulator. Verify that the app displays no data. **RefreshAsync()** has not yet been called. 6. Perform the refresh gesture by pulling down the list of items, which calls **RefreshAsync()**. This calls **Authenticate()** to start authentication and the login screen is displayed. After you have successfully authenticated, the app displays the list of todo items and you can make updates to the data. ## Next steps In the next tutorial, [Service-side authorization of Mobile Services users][Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-dotnet-backend-xamarin-ios-get-started.md [Get started with authentication]: mobile-services-dotnet-backend-xamarin-ios-get-started-users.md [Get started with push notifications]: mobile-services-dotnet-backend-xamarin-ios-get-started-push.md [Authorize users with scripts]: mobile-services-dotnet-backend-service-side-authorization.md [JavaScript and HTML]: mobile-services-dotnet-backend-windows-store-javascript-get-started-users.md ================================================ FILE: docs/mobile-services-dotnet-backend-xamarin-ios-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a Xamarin iOS app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple _To do list_ app that stores app data in the new mobile service. The mobile service that you will create uses the supported .NET languages using Visual Studio for server-side business logic and to manage the mobile service. To create a mobile service that lets you write your server-side business logic in JavaScript, see the [JavaScript backend version] of this topic. >[AZURE.NOTE]This topic shows you how to create a new mobile service project by using the Azure classic portal. By using Visual Studio 2013 Update 2, you can also add a new mobile service project to an existing Visual Studio solution. For more information, see [Quickstart: Add a mobile service (.NET backend)](http://msdn.microsoft.com/library/windows/apps/dn629482.aspx) A screenshot from the completed app is below: ![][0] Completing this tutorial is a prerequisite for all other Mobile Services tutorials for Xamarin iOS apps. >[AZURE.NOTE]To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see Azure Free Trial.
This tutorial requires Visual Studio Professional 2013. A free trial version is available. ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** page, select **Create a free 20 MB SQL Database**, select **.NET** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. > [AZURE.NOTE] As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-dotnet-backend-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Xamarin iOS app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will download a new Xamarin iOS app and a service project for your mobile service. 1. If you haven't already done so, install Visual Studio with Xamarin. Instructions can be found on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). You can also use Xamarin Studio on a Mac OS X machine, see [Setup, install, and verifications for Mac users](https://msdn.microsoft.com/library/mt488770.aspx). 2. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 3. In the quickstart tab, click **Xamarin** under **Choose platform** and expand **Create a new Xamarin app**. ![][6] This displays the three easy steps to create a Xamarin iOS app connected to your mobile service. ![][7] 4. Under **Download and publish your service to the cloud**, select **iOS** and click **Download**. This downloads a solution contains projects for both the mobile service and for the sample _To do list_ application that is connected to your mobile service. Save the compressed project file to your local computer, and make a note of where you save it. 5. Download your publish profile, save the downloaded file to your local computer, and make a note of where you save it. ## Test the mobile service The mobile service project lets you run your new mobile service locally. This makes it easy to debug your service code before you even publish it to Azure. 1. On your Windows PC, download your personalized server project, extract it, and then open it in Visual Studio. 2. Press the **F5** key to rebuild the project and start the mobile service locally. A web page is displayed after the mobile service starts successfully. ## Publish your mobile service 1. In Visual Studio, right-click the project, click **Publish** > **Microsoft Azure Mobile Services**. Instead of using Visual Studio, [you may also use Git](./ mobile-services-dotnet-backend-store-code-source-control.md). 2. Sign in with Azure credentials and select your service from **Existing Mobile Services**. Visual Studio downloads your publish settings directly from Azure. Finally, click **Publish**. ## Run the Xamarin iOS app The final stage of this tutorial is to build and run your new app. 1. Navigate to the client project within the mobile service solution, in either Visual Studio or Xamarin Studio. ![][8] ![][9] 2. Press the **Run** button to build the client project and start the app in the iPhone emulator. 3. In the app, type meaningful text, such as _Complete the tutorial_ and then click the plus (**+**) icon. ![][10] This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. >[AZURE.NOTE]You can review the code that accesses your mobile service to query and insert data in the QSTodoService.cs C# file. ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync]
Learn how the quickstart uses offline data sync to make the app responsive and robust. * [Get started with authentication]
Learn how to authenticate users of your app with an identity provider. * [Get started with push notifications]
Learn how to send a very basic push notification to your app. * [Troubleshoot a Mobile Services .NET backend]
Learn how to diagnose and fix issues that can arise with a Mobile Services .NET backend. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Next Steps]:#next-steps [0]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-quickstart-completed-ios.png [6]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-portal-quickstart-xamarin-ios.png [7]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-quickstart-steps-xamarin-ios.png [8]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-xamarin-project-ios-vs.png [9]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-xamarin-project-ios-xs.png [10]: ./media/mobile-services-dotnet-backend-xamarin-ios-get-started/mobile-quickstart-startup-ios.png [Get started with offline data sync]: mobile-services-xamarin-ios-get-started-offline-data.md [Get started with authentication]: mobile-services-dotnet-backend-xamarin-ios-get-started-users.md [Get started with push notifications]: mobile-services-dotnet-backend-xamarin-ios-get-started-push.md [Visual Studio Professional 2013]: https://go.microsoft.com/fwLink/p/?LinkID=257546 [Mobile Services SDK]: http://go.microsoft.com/fwlink/?LinkId=257545 [JavaScript and HTML]: mobile-services-win8-javascript/ [Azure classic portal]: https://manage.windowsazure.com/ [JavaScript backend version]: mobile-services-ios-get-started.md [Troubleshoot a Mobile Services .NET backend]: mobile-services-dotnet-backend-how-to-troubleshoot.md ================================================ FILE: docs/mobile-services-dotnet-how-to-use-client-library.md ================================================ # How to use the managed client library for Azure Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Android](mobile-services-android-how-to-use-client-library.md) - [HTML/JavaScript](mobile-services-html-how-to-use-client-library.md) - [iOS](mobile-services-ios-how-to-use-client-library.md) - [Managed (Windows/Xamarin)](mobile-services-dotnet-how-to-use-client-library.md) ## Overview This guide shows you how to perform common scenarios using the managed client library for Azure Mobile Services in Windows and Xamarin apps. The scenarios covered include querying for data, inserting, updating, and deleting data, authenticating users, and handling errors. If you are new to Mobile Services, you should consider first completing the [Mobile Services quickstart](mobile-services-dotnet-backend-xamarin-ios-get-started.md) tutorial. ## What is Mobile Services Azure Mobile Services is a highly scalable mobile application development platform that lets you add enhanced functionality to your mobile device apps by using Azure. With Mobile Services you can: + **Build native and cross platform apps** - Connect your iOS, Android, Windows, or cross-platform Xamarin or Cordova (Phonegap) apps to your backend mobile service using native SDKs. + **Send push notifications to your users** - Send push notifications to your users of your app. + **Authenticate your users** - Leverage popular identity providers like Facebook and Twitter to authenticate your app users. + **Store data in the cloud** - Store user data in a SQL Database (by default) or in Mongo DB, DocumentDB, Azure Tables, or Azure Blobs. + **Build offline-ready apps with sync** - Make your apps work offline and use Mobile Services to sync data in the background. + **Monitor and scale your apps** - Monitor app usage and scale your backend as demand grows. ## Mobile Services Concepts The following are important features and concepts in the Mobile Services: + **Application key:** a unique value that is used to limit access to your mobile service from random clients; this "key" is not a security token and is not used to authenticate users of your app. + **Backend:** the mobile service instance that supports your app. A mobile service is implemented either as an ASP.NET Web API project (*.NET backend* ) or as a Node.js project (*JavaScript backend*). + **Identity provider:** an external service, trusted by Mobile Services, that authenticates your app's users. Supported providers include: Facebook, Twitter, Google, Microsoft Account, and Azure Active Directory. + **Push notification:** Service-initiated message that is sent to a registered device or user using Azure Notification Hubs. + **Scale:** The ability to add, for an additional cost, more processing power, performance, and storage as your app becomes more popular. + **Scheduled job:** Custom code that is run either on a pre-determined schedule or on-demand. For more information, see [Mobile Services Concepts](./ mobile-services-concepts-links.md). ## Setup and Prerequisites We assume that you have created a mobile service and a table. For more information see [Create a table](http://go.microsoft.com/fwlink/?LinkId=298592). In the code used in this topic, the table is named `TodoItem` and it will have the following columns: `Id`, `Text`, and `Complete`. The corresponding typed client-side .NET type is the following: public class TodoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } } Note that the [JsonPropertyAttribute](http://www.newtonsoft.com/json/help/html/Properties_T_Newtonsoft_Json_JsonPropertyAttribute.htm) is used to define the mapping between the PropertyName mapping between the client type and the table. When dynamic schema is enabled in a JavaScript backend mobile service, Azure Mobile Services automatically generates new columns based on the object in insert or update requests. For more information, see [Dynamic schema](http://go.microsoft.com/fwlink/?LinkId=296271). In a .NET backend mobile service, the table is defined in the data model of the project. ## How to: Create the Mobile Services client The following code creates the `MobileServiceClient` object that is used to access your mobile service. MobileServiceClient client = new MobileServiceClient( "AppUrl", "AppKey" ); In the code above, replace `AppUrl` and `AppKey` with the mobile service URL and application key, in that order. Both of these are available on the Azure classic portal, by selecting your mobile service and then clicking on "Dashboard". >[AZURE.IMPORTANT]The application key is intended to filter-out random request against your mobile service, and it is distributed with the application. Because this key is not encrypted, it cannot be considered secure. To truly secure your mobile service data, you must instead authenticate users before allowing access. For more information, see [How to: Authenticate users](#authentication). ## How to: Create a table reference All of the code that accesses or modifies data in the Mobile Services table calls functions on the `MobileServiceTable` object. You get a reference to the table by calling the [GetTable](https://msdn.microsoft.com/library/azure/jj554275.aspx) method on an instance of the `MobileServiceClient`, as follows: IMobileServiceTable todoTable = client.GetTable(); This is the typed serialization model; see the discussion of the [untyped serialization model](#untyped) below. ## How to: Query data from a mobile service This section describes how to issue queries to the mobile service, which includes the following functionality: - [Filter returned data] - [Sort returned data] - [Return data in pages] - [Select specific columns] - [Look up data by ID] >[AZURE.NOTE] A server-driven page size is enforced to prevent all rows from being returned. This keeps default requests for large data sets from negatively impacting the service. To return more than 50 rows, use the `Take` method, as described in [Return data in pages]. ### How to: Filter returned data The following code illustrates how to filter data by including a `Where` clause in a query. It returns all items from `todoTable` whose `Complete` property is equal to `false`. The `Where` function applies a row filtering predicate to the query against the table. // This query filters out completed TodoItems and // items without a timestamp. List items = await todoTable .Where(todoItem => todoItem.Complete == false) .ToListAsync(); You can view the URI of the request sent to the mobile service by using message inspection software, such as browser developer tools or [Fiddler]. If you look at the request URI below, notice that we are modifying the query string itself: GET /tables/todoitem?$filter=(complete+eq+false) HTTP/1.1 This request would normally be translated roughly into the following SQL query on the server side: SELECT * FROM TodoItem WHERE ISNULL(complete, 0) = 0 The function which is passed to the `Where` method can have an arbitrary number of conditions. For example, the line below: // This query filters out completed TodoItems where Text isn't null List items = await todoTable .Where(todoItem => todoItem.Complete == false && todoItem.Text != null) .ToListAsync(); Would be roughly translated (for the same request shown before) as SELECT * FROM TodoItem WHERE ISNULL(complete, 0) = 0 AND ISNULL(text, 0) = 0 The `where` statement above will find items with `Complete` status set to false with non-null `Text`. We also could have written that in multiple lines instead: List items = await todoTable .Where(todoItem => todoItem.Complete == false) .Where(todoItem => todoItem.Text != null) .ToListAsync(); The two methods are equivalent and may be used interchangeably. The former option -- of concatenating multiple predicates in one query -- is more compact and recommended. The `where` clause supports operations that be translated into the Mobile Services OData subset. This includes relational operators (==, !=, <, <=, >, >=), arithmetic operators (+, -, /, *, %), number precision (Math.Floor, Math.Ceiling), string functions (Length, Substring, Replace, IndexOf, StartsWith, EndsWith), date properties (Year, Month, Day, Hour, Minute, Second), access properties of an object, and expressions combining all of these. ### How to: Sort returned data The following code illustrates how to sort data by including an `OrderBy` or `OrderByDescending` function in the query. It returns items from `todoTable` sorted ascending by the `Text` field. // Sort items in ascending order by Text field MobileServiceTableQuery query = todoTable .OrderBy(todoItem => todoItem.Text) List items = await query.ToListAsync(); // Sort items in descending order by Text field MobileServiceTableQuery query = todoTable .OrderByDescending(todoItem => todoItem.Text) List items = await query.ToListAsync(); ### How to: Return data in pages By default, the server returns only the first 50 rows. You can increase the number of returned rows by calling the [Take] method. Use `Take` along with the [Skip] method to request a specific "page" of the total dataset returned by the query. The following query, when executed, returns the top three items in the table. // Define a filtered query that returns the top 3 items. MobileServiceTableQuery query = todoTable .Take(3); List items = await query.ToListAsync(); The following revised query skips the first three results and returns the next three after that. This is effectively the second "page" of data, where the page size is three items. // Define a filtered query that skips the top 3 items and returns the next 3 items. MobileServiceTableQuery query = todoTable .Skip(3) .Take(3); List items = await query.ToListAsync(); You can also use the [IncludeTotalCount] method to ensure that the query will get the total count for all the records that would have been returned, ignoring any take paging/limit clause specified: query = query.IncludeTotalCount(); This is a simplified scenario of passing hard-coded paging values to the `Take` and `Skip` methods. In a real-world app, you can use queries similar to the above with a pager control or comparable UI to let users navigate to previous and next pages. #### Paging considerations for a .NET backend mobile service To override the 50 row limit in a .NET backend mobile service, you must also apply the [EnableQueryAttribute](https://msdn.microsoft.com/library/system.web.http.odata.enablequeryattribute.aspx) to the public GET method and specify the paging behavior. When applied to the method, the following sets the maximum returned rows to 1000: [EnableQuery(MaxTop=1000)] ### How to: Select specific columns You can specify which set of properties to include in the results by adding a `Select` clause to your query. For example, the following code shows how to select just one field and also how to select and format multiple fields: // Select one field -- just the Text MobileServiceTableQuery query = todoTable .Select(todoItem => todoItem.Text); List items = await query.ToListAsync(); // Select multiple fields -- both Complete and Text info MobileServiceTableQuery query = todoTable .Select(todoItem => string.Format("{0} -- {1}", todoItem.Text.PadRight(30), todoItem.Complete ? "Now complete!" : "Incomplete!")); List items = await query.ToListAsync(); All the functions described so far are additive, so we can just keep calling them and we'll each time affect more of the query. One more example: MobileServiceTableQuery query = todoTable .Where(todoItem => todoItem.Complete == false) .Select(todoItem => todoItem.Text) .Skip(3). .Take(3); List items = await query.ToListAsync(); ### How to: Look up data by ID The `LookupAsync` function can be used to look up objects from the database with a particular ID. // This query filters out the item with the ID of 37BBF396-11F0-4B39-85C8-B319C729AF6D TodoItem item = await todoTable.LookupAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D"); ## How to: Insert data into a mobile service > [AZURE.NOTE] If you want to perform insert, lookup, delete, or update operations on a type, then you need to create a member called **Id**. This is why the example class **TodoItem** has a member of name **Id**. A valid id value must always be present in update and delete operations. The following code illustrates how to insert new rows into a table. The parameter contains the data to be inserted as a .NET object. await todoTable.InsertAsync(todoItem); If a unique custom ID value is not included in the `todoItem` passed to the `todoTable.InsertAsync` call, a value for ID is generated by the server and is set in the `todoItem` object returned to the client. To insert untyped data, you may take advantage of Json.NET as shown below. JObject jo = new JObject(); jo.Add("Text", "Hello World"); jo.Add("Complete", false); var inserted = await table.InsertAsync(jo); Here is an example using an email address as a unique string id. JObject jo = new JObject(); jo.Add("id", "myemail@emaildomain.com"); jo.Add("Text", "Hello World"); jo.Add("Complete", false); var inserted = await table.InsertAsync(jo); ### Working with ID values Mobile Services supports unique custom string values for the table's **id** column. This allows applications to use custom values such as email addresses or user names for the ID. String IDs provide you with the following benefits: + IDs are generated without making a round-trip to the database. + Records are easier to merge from different tables or databases. + IDs values can integrate better with an application's logic. When a string ID value is not set on an inserted record, Mobile Services generates a unique value for the ID. You can use the `Guid.NewGuid()` method To generate your own ID values, either on the client or in a .NET mobile backend service. To learn more about generating GUIDs in a JavaScript backend mobile service, see [How to: Generate unique ID values](mobile-services-how-to-use-server-scripts.md#generate-guids). You can also use integer IDs for your tables. To use an integer ID, you must create your table with the `mobile table create` command using the `--integerId` option. This command is used with the Command-line Interface (CLI) for Azure. For more information on using the CLI, see [CLI to manage Mobile Services tables](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Mobile_Tables). ## How to: Modify data in a mobile service The following code illustrates how to update an existing instance with the same ID with new information. The parameter contains the data to be updated as a .NET object. await todoTable.UpdateAsync(todoItem); To insert untyped data, you may take advantage of Json.NET like so. Note that when making an update, an ID must be specified, as that is how the mobile service identifies which instance to update. The ID can be obtained from the result of the `InsertAsync` call. JObject jo = new JObject(); jo.Add("Id", "37BBF396-11F0-4B39-85C8-B319C729AF6D"); jo.Add("Text", "Hello World"); jo.Add("Complete", false); var inserted = await table.UpdateAsync(jo); If you attempt to update an item without providing the "Id" value, there is no way for the service to tell which instance to update, so the Mobile Services SDK will throw an `ArgumentException`. ## How to: Delete data in a mobile service The following code illustrates how to delete an existing instance. The instance is identified by the "Id" field set on the `todoItem`. await todoTable.DeleteAsync(todoItem); To delete untyped data, you may take advantage of Json.NET like so. Note that when making a delete request, an ID must be specified, as that is how the mobile service identifies which instance to delete. A delete request needs only the ID; other properties are not passed to the service, and if any are passed, they are ignored at the service. The result of a `DeleteAsync` call is usually `null` as well. The ID to pass in can be obtained from the result of the `InsertAsync` call. JObject jo = new JObject(); jo.Add("Id", "37BBF396-11F0-4B39-85C8-B319C729AF6D"); await table.DeleteAsync(jo); If you attempt to delete an item without the "Id" field already set, there is no way for the service to tell which instance to delete, so you will get back a `MobileServiceInvalidOperationException` from the service. Similarly, if you attempt to delete an untyped item without the "Id" field already set, you will again get back a `MobileServiceInvalidOperationException` from the service. ## How to: Call a custom API A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON. For an example of how to create a custom API in your mobile service, see [How to: define a custom API endpoint](mobile-services-dotnet-backend-define-custom-api.md). You call a custom API by calling one of the [InvokeApiAsync] method overloads on the client. For example, the following line of code sends a POST request to the **completeAll** API on the mobile service: var result = await App.MobileService .InvokeApiAsync("completeAll", System.Net.Http.HttpMethod.Post, null); Note that this a typed method call, which requires that the **MarkAllResult** return type be defined. Both typed and untyped methods are supported. This is an almost trivial example as it is typed, sends no payload, has no query parameters, and doesn't change the request headers. For more realistic examples and a more a complete discussion of [InvokeApiAsync], see [Custom API in Azure Mobile Services Client SDKs]. ## How to: Register for push notifications The Mobile Services client enables you to register for push notifications with Azure Notification Hubs. When registering, you obtain a handle that you obtain from the platform-specific Push Notification Service (PNS). You then provide this value along with any tags when you create the registration. The following code registers your Windows app for push notifications with the Windows Notification Service (WNS): private async void InitNotificationsAsync() { // Request a push notification channel. var channel = await PushNotificationChannelManager .CreatePushNotificationChannelForApplicationAsync(); // Register for notifications using the new channel and a tag collection. var tags = new List{ "mytag1", "mytag2"}; await MobileService.GetPush().RegisterNativeAsync(channel.Uri, tags); } Note that in this example, two tags are included with the registration. For more information on Windows apps, see [Add push notifications to your app](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md). Xamarin apps require some additional code to be able to register a Xamarin app running on iOS or Android app with the Apple Push Notification Service (APNS) and Google Cloud Messaging (GCM) services, respectively. For more information see **Add push notifications to your app** ([Xamarin.iOS](partner-xamarin-mobile-services-ios-get-started-push.md#add-push) | [Xamarin.Android](partner-xamarin-mobile-services-android-get-started-push.md#add-push)). >[AZURE.NOTE]When you need to send notifications to specific registered users, it is important to require authentication before registration, and then verify that the user is authorized to register with a specific tag. For example, you must check to make sure a user doesn't register with a tag that is someone else's user ID. For more information, see [Send push notifications to authenticated users](mobile-services-dotnet-backend-windows-store-dotnet-push-notifications-app-users.md). ## How to: Use periodic notifications in a Windows app Windows supports period notifications (pull notifications) to update live tiles. With periodic notifications enabled, Windows will periodically access a custom API endpoint to update the app tile on the start menu. To use periodic notifications, you must [define a custom API](mobile-services-javascript-backend-define-custom-api.md) that returns XML data in a tile-specific format. For more information, see [Periodic notifications](https://msdn.microsoft.com/library/windows/apps/hh761461.aspx). The following example turns on period notifications to request tile template data from a *tiles* custom endpoint: TileUpdateManager.CreateTileUpdaterForApplication().StartPeriodicUpdate( new System.Uri(MobileService.ApplicationUri, "/api/tiles"), PeriodicUpdateRecurrence.Hour ); Select a [PeriodicUpdateRecurrance](https://msdn.microsoft.com/library/windows/apps/windows.ui.notifications.periodicupdaterecurrence.aspx) value that best matches the update frequency of your data. ## How to: Use optimistic concurrency Two or more clients may write changes to the same item, at the same time, in some scenarios. Without any conflict detection, the last write would overwrite any previous updates even if this was not the desired result. Optimistic Concurrency Control assumes that each transaction can commit and therefore does not use any resource locking. Before committing a transaction, optimistic concurrency control verifies that no other transaction has modified the data. If the data has been modified, the committing transaction is rolled back. Mobile Services supports optimistic concurrency control by tracking changes to each item using the `__version` system property column that is defined for each table created by Mobile Services. Each time a record is updated, Mobile Services sets the `__version` property for that record to a new value. During each update request, the `__version` property of the record included with the request is compared to the same property for the record on the server. If the version passed with the request does not match the server, then the Mobile Services .NET client library throws a `MobileServicePreconditionFailedException`. The type included with the exception is the record from the server containing the server's version of the record. The application can then use this information to decide whether to execute the update request again with the correct `__version` value from the server to commit changes. To enable optimistic concurrency the application defines a column on the table class for the `__version` system property. The following definition provides an example. public class TodoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } // *** Enable Optimistic Concurrency *** // [JsonProperty(PropertyName = "__version")] public byte[] Version { set; get; } } Applications using untyped tables enable optimistic concurrency by setting the `Version` flag on the `SystemProperties` of the table as follows. //Enable optimistic concurrency by retrieving __version todoTable.SystemProperties |= MobileServiceSystemProperties.Version; The following code shows how to resolve a write conflict once detected. The correct `__version` value must be included in the `UpdateAsync()` call to commit a resolution. private async void UpdateToDoItem(TodoItem item) { MobileServicePreconditionFailedException exception = null; try { //update at the remote table await todoTable.UpdateAsync(item); } catch (MobileServicePreconditionFailedException writeException) { exception = writeException; } if (exception != null) { // Conflict detected, the item has changed since the last query // Resolve the conflict between the local and server item await ResolveConflict(item, exception.Item); } } private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem) { //Ask user to choose the resoltion between versions MessageDialog msgDialog = new MessageDialog(String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n", serverItem.Text, localItem.Text), "CONFLICT DETECTED - Select a resolution:"); UICommand localBtn = new UICommand("Commit Local Text"); UICommand ServerBtn = new UICommand("Leave Server Text"); msgDialog.Commands.Add(localBtn); msgDialog.Commands.Add(ServerBtn); localBtn.Invoked = async (IUICommand command) => { // To resolve the conflict, update the version of the // item being committed. Otherwise, you will keep // catching a MobileServicePreConditionFailedException. localItem.Version = serverItem.Version; // Updating recursively here just in case another // change happened while the user was making a decision UpdateToDoItem(localItem); }; ServerBtn.Invoked = async (IUICommand command) => { RefreshTodoItems(); }; await msgDialog.ShowAsync(); } For a more complete example of using optimistic concurrency for Mobile Services, see the [Optimistic Concurrency Tutorial]. ## How to: Bind mobile service data to a Windows user interface This section shows how to display returned data objects using UI elements in a Windows app. To query incomplete items in `todoTable` and display it in a very simple list, you can run the following example code to bind the source of the list with a query. Using `MobileServiceCollection` creates a mobile services-aware binding collection. // This query filters out completed TodoItems. MobileServiceCollection items = await todoTable .Where(todoItem => todoItem.Complete == false) .ToCollectionAsync(); // itemsControl is an IEnumerable that could be bound to a UI list control IEnumerable itemsControl = items; // Bind this to a ListBox ListBox lb = new ListBox(); lb.ItemsSource = items; Some controls in the managed runtime support an interface called [ISupportIncrementalLoading](http://msdn.microsoft.com/library/windows/apps/Hh701916). This interface allows controls to request extra data when the user scrolls. There is built-in support for this interface for universal Windows 8.1 apps via `MobileServiceIncrementalLoadingCollection`, which automatically handles the calls from the controls. To use `MobileServiceIncrementalLoadingCollection` in Windows apps, do the following: MobileServiceIncrementalLoadingCollection items; items = todoTable.Where(todoItem => todoItem.Complete == false) .ToIncrementalLoadingCollection(); ListBox lb = new ListBox(); lb.ItemsSource = items; To use the new collection on Windows Phone 8 and "Silverlight" apps, use the `ToCollection` extension methods on `IMobileServiceTableQuery` and `IMobileServiceTable`. To actually load data, call `LoadMoreItemsAsync()`. MobileServiceCollection items = todoTable.Where(todoItem => todoItem.Complete==false).ToCollection(); await items.LoadMoreItemsAsync(); When you use the collection created by calling `ToCollectionAsync` or `ToCollection`, you get a collection which can be bound to UI controls. This collection is paging-aware, i.e., a control can ask the collection to "load more items", and the collection will do it for the control. At that point there is no user code involved, the control will start the flow. However, since the collection is loading data from the network, it's expected that some times this loading will fail. To handle such failures, you may override the `OnException` method on `MobileServiceIncrementalLoadingCollection` to handle exceptions resulting from calls to `LoadMoreItemsAsync` performed by controls. Finally, imagine that your table has many fields, but you only want to display some of them in your control. You may use the guidance in the section "[Select specific columns](#selecting)" above to select specific columns to display in the UI. ## How to: Authenticate users Mobile Services supports authenticating and authorizing app users using a variety of external identity providers: Facebook, Google, Microsoft Account, Twitter, and Azure Active Directory. You can set permissions on tables to restrict access for specific operations to only authenticated users. You can also use the identity of authenticated users to implement authorization rules in server scripts. For more information, see the tutorial [Add authentication to your app]. Two authentication flows are supported: a _server flow_ and a _client flow_. The server flow provides the simplest authentication experience, as it relies on the provider's web authentication interface. The client flow allows for deeper integration with device-specific capabilities as it relies on provider-specific device-specific SDKs. ### Server flow To have Mobile Services manage the authentication process in your Windows apps, you must register your app with your identity provider. Then in your mobile service, you need to configure the application ID and secret provided by your provider. For more information, see the tutorial [Add authentication to your app]. Once you have registered your identity provider, simply call the [LoginAsync method] with the [MobileServiceAuthenticationProvider] value of your provider. For example, the following code initiates a server flow sign-in by using Facebook. private MobileServiceUser user; private async System.Threading.Tasks.Task Authenticate() { while (user == null) { string message; try { user = await client .LoginAsync(MobileServiceAuthenticationProvider.Facebook); message = string.Format("You are now logged in - {0}", user.UserId); } catch (InvalidOperationException) { message = "You must log in. Login Required"; } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); } } If you are using an identity provider other than Facebook, change the value of [MobileServiceAuthenticationProvider] above to the value for your provider. In this case, Mobile Services manages the OAuth 2.0 authentication flow by displaying the sign-in page of the selected provider and generating a Mobile Services authentication token after successful sign-on with the identity provider. The [LoginAsync method] returns a [MobileServiceUser], which provides both the [userId] of the authenticated user and the [MobileServiceAuthenticationToken], as a JSON web token (JWT). This token can be cached and re-used until it expires. For more information, see [Caching the authentication token]. ### Client flow Your app can also independently contact the identity provider and then provide the returned token to Mobile Services for authentication. This client flow enables you to provide a single sign-in experience for users or to retrieve additional user data from the identity provider. #### Single sign-in using a token from Facebook or Google In the most simplified form, you can use the client flow as shown in this snippet for Facebook or Google. var token = new JObject(); // Replace access_token_value with actual value of your access token obtained // using the Facebook or Google SDK. token.Add("access_token", "access_token_value"); private MobileServiceUser user; private async System.Threading.Tasks.Task Authenticate() { while (user == null) { string message; try { // Change MobileServiceAuthenticationProvider.Facebook // to MobileServiceAuthenticationProvider.Google if using Google auth. user = await client .LoginAsync(MobileServiceAuthenticationProvider.Facebook, token); message = string.Format("You are now logged in - {0}", user.UserId); } catch (InvalidOperationException) { message = "You must log in. Login Required"; } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); } } #### Single sign-in using Microsoft Account with the Live SDK To be able to authenticate users, you must register your app at the Microsoft account Developer Center. You must then connect this registration with your mobile service. Complete the steps in [Register your app to use a Microsoft account login](mobile-services-how-to-register-microsoft-authentication.md) to create a Microsoft account registration and connect it to your mobile service. If you have both Windows Store and Windows Phone 8/Silverlight versions of your app, register the Windows Store version first. The following code authenticates using Live SDK and uses the returned token to sign-in to your mobile service. private LiveConnectSession session; //private static string clientId = ""; private async System.Threading.Tasks.Task AuthenticateAsync() { // Get the URL the mobile service. var serviceUrl = App.MobileService.ApplicationUri.AbsoluteUri; // Create the authentication client for Windows Store using the mobile service URL. LiveAuthClient liveIdClient = new LiveAuthClient(serviceUrl); //// Create the authentication client for Windows Phone using the client ID of the registration. //LiveAuthClient liveIdClient = new LiveAuthClient(clientId); while (session == null) { // Request the authentication token from the Live authentication service. // The wl.basic scope is requested. LiveLoginResult result = await liveIdClient.LoginAsync(new string[] { "wl.basic" }); if (result.Status == LiveConnectSessionStatus.Connected) { session = result.Session; // Get information about the logged-in user. LiveConnectClient client = new LiveConnectClient(session); LiveOperationResult meResult = await client.GetAsync("me"); // Use the Microsoft account auth token to sign in to Mobile Services. MobileServiceUser loginResult = await App.MobileService .LoginWithMicrosoftAccountAsync(result.Session.AuthenticationToken); // Display a personalized sign-in greeting. string title = string.Format("Welcome {0}!", meResult.Result["first_name"]); var message = string.Format("You are now logged in - {0}", loginResult.UserId); var dialog = new MessageDialog(message, title); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); } else { session = null; var dialog = new MessageDialog("You must log in.", "Login Required"); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); } } } ### Caching the authentication token In some cases, the call to the login method can be avoided after the first time the user authenticates. You can use [PasswordVault] for Windows Store apps to cache the current user identity the first time they log in and every subsequent time you check whether you already have the user identity in our cache. When the cache is empty, you still need to send the user through the login process. // After logging in PasswordVault vault = new PasswordVault(); vault.Add(new PasswordCredential("Facebook", user.UserId, user.MobileServiceAuthenticationToken)); // Log in var creds = vault.FindAllByResource("Facebook").FirstOrDefault(); if (creds != null) { user = new MobileServiceUser(creds.UserName); user.MobileServiceAuthenticationToken = vault.Retrieve("Facebook", creds.UserName).Password; } else { // Regular login flow user = new MobileServiceuser( await client .LoginAsync(MobileServiceAuthenticationProvider.Facebook, token); var token = new JObject(); // Replace access_token_value with actual value of your access token token.Add("access_token", "access_token_value"); } // Log out client.Logout(); vault.Remove(vault.Retrieve("Facebook", user.UserId)); For Windows Phone apps, you may encrypt and cache data using the [ProtectedData] class and store sensitive information in isolated storage. ## How to: Handle errors There are several ways to encounter, validate, and work around errors in Mobile Services. As an example, server scripts are registered in a mobile service and can be used to perform a wide range of operations on data being inserted and updated, including validation and data modification. Imagine defining and registering a server script that validate and modify data, like so: function insert(item, user, request) { if (item.text.length > 10) { request.respond(statusCodes.BAD_REQUEST, { error: "Text cannot exceed 10 characters" }); } else { request.execute(); } } This server-side script validates the length of string data sent to the mobile service and rejects strings that are too long, in this case longer than 10 characters. Now that the mobile service is validating data and sending error responses on the server-side, you can update your .NET app to be able to handle error responses from validation. private async void InsertTodoItem(TodoItem todoItem) { // This code inserts a new TodoItem into the database. When the operation completes // and Mobile Services has assigned an Id, the item is added to the CollectionView try { await todoTable.InsertAsync(todoItem); items.Add(todoItem); } catch (MobileServiceInvalidOperationException e) { // Handle error } } ## How to: Work with untyped data The .NET client is designed for strongly typed scenarios. However, sometimes, a more loosely typed experience is convenient; for example, this could be when dealing with objects with open schema. That scenario is enabled as follows. In queries, you forego LINQ and use the wire format. // Get an untyped table reference IMobileServiceTable untypedTodoTable = client.GetTable("TodoItem"); // Lookup untyped data using OData JToken untypedItems = await untypedTodoTable.ReadAsync("$filter=complete eq 0&$orderby=text"); You get back JSON values that you can use like a property bag. For more information on JToken and Json.NET, see [Json.NET](http://json.codeplex.com/) ## How to: Design unit tests The value returned by `MobileServiceClient.GetTable` and the queries are interfaces. That makes them easily "mockable" for testing purposes, so you could create a `MyMockTable : IMobileServiceTable` that implements your testing logic. ## How to: Customize the client This section shows ways in which you can customize the request headers and customize the serialization of JSON objects in the response. ### How to: Customize request headers To support your specific app scenario, you might need to customize communication with the mobile service. For example, you may want to add a custom header to every outgoing request or even change responses status codes. You can do this by providing a custom DelegatingHandler, as in the following example: public async Task CallClientWithHandler() { MobileServiceClient client = new MobileServiceClient( "AppUrl", "AppKey" , new MyHandler() ); IMobileServiceTable todoTable = client.GetTable(); var newItem = new TodoItem { Text = "Hello world", Complete = false }; await table.InsertAsync(newItem); } public class MyHandler : DelegatingHandler { protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Add a custom header to the request. request.Headers.Add("x-my-header", "my value"); var response = await base.SendAsync(request, cancellationToken); // Set a differnt response status code. response.StatusCode = HttpStatusCode.ServiceUnavailable; return response; } } This code adds a new **x-my-header** header in the request and arbitrarily sets the response code to unavailable. In a real-world scenario, you would set the response status code based on some custom logic required by your app. ### How to: Customize serialization The Mobile Services client library uses Json.NET to convert a JSON response into .NET objects on the client. You can configure the behavior of this serialization between .NET types and JSON in the messages. The [MobileServiceClient](http://msdn.microsoft.com/library/microsoft.windowsazure.mobileservices.mobileserviceclient.aspx) class exposes a `SerializerSettings` property of type [JsonSerializerSettings](http://james.newtonking.com/projects/json/help/?topic=html/T_Newtonsoft_Json_JsonSerializerSettings.htm) Using this property, you may set one of the many Json.NET properties, such as the following: var settings = new JsonSerializerSettings(); settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); client.SerializerSettings = settings; This property converts all properties to lower case during serialization. [What is Mobile Services]: #what-is [Concepts]: #concepts [How to: Create the Mobile Services client]: #create-client [How to: Create a table reference]: #instantiating [How to: Query data from a mobile service]: #querying [Filter returned data]: #filtering [Sort returned data]: #sorting [Return data in pages]: #paging [Select specific columns]: #selecting [Look up data by ID]: #lookingup [How to: Bind data to user interface in a mobile service]: #binding [How to: Insert data into a mobile service]: #inserting [How to: Modify data in a mobile service]: #modifying [How to: Delete data in a mobile service]: #deleting [How to: Use Optimistic Concurrency]: #optimisticconcurrency [How to: Authenticate users]: #authentication [How to: Handle errors]: #errors [How to: Design unit tests]: #unit-testing [How to: Query data from a mobile service]: #querying [How to: Customize the client]: #customizing [How to: Work with untyped data]: #untyped [Customize request headers]: #headers [Customize serialization]: #serialization [Next steps]: #nextsteps [Caching the authentication token]: #caching [How to: Call a custom API]: #custom-api [Add authentication to your app]: mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md [PasswordVault]: http://msdn.microsoft.com/library/windows/apps/windows.security.credentials.passwordvault.aspx [ProtectedData]: http://msdn.microsoft.com/library/system.security.cryptography.protecteddata%28VS.95%29.aspx [LoginAsync method]: http://msdn.microsoft.com/library/windowsazure/microsoft.windowsazure.mobileservices.mobileserviceclientextensions.loginasync.aspx [MobileServiceAuthenticationProvider]: http://msdn.microsoft.com/library/windowsazure/microsoft.windowsazure.mobileservices.mobileserviceauthenticationprovider.aspx [MobileServiceUser]: http://msdn.microsoft.com/library/windowsazure/microsoft.windowsazure.mobileservices.mobileserviceuser.aspx [UserID]: http://msdn.microsoft.com/library/windowsazure/microsoft.windowsazure.mobileservices.mobileserviceuser.userid.aspx [MobileServiceAuthenticationToken]: http://msdn.microsoft.com/library/windowsazure/microsoft.windowsazure.mobileservices.mobileserviceuser.mobileserviceauthenticationtoken.aspx [ASCII control codes C0 and C1]: http://en.wikipedia.org/wiki/Data_link_escape_character#C1_set [CLI to manage Mobile Services tables]: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Commands_to_manage_mobile_services [Optimistic Concurrency Tutorial]: mobile-services-windows-store-dotnet-handle-database-conflicts.md [MobileServiceClient]: https://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobileservices.mobileserviceclient.aspx [IncludeTotalCount]: http://msdn.microsoft.com/library/windowsazure/dn250560.aspx [Skip]: http://msdn.microsoft.com/library/windowsazure/dn250573.aspx [Take]: http://msdn.microsoft.com/library/windowsazure/dn250574.aspx [Fiddler]: http://www.telerik.com/fiddler [Custom API in Azure Mobile Services Client SDKs]: http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/19/custom-api-in-azure-mobile-services-client-sdks.aspx [InvokeApiAsync]: http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobileservices.mobileserviceclient.invokeapiasync.aspx ================================================ FILE: docs/mobile-services-how-to-register-active-directory-authentication.md ================================================ # Register your apps to use an Azure Active Directory Account login >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md) - [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) - [Google](./ mobile-services-how-to-register-google-authentication.md) - [Microsoft account](./ mobile-services-how-to-register-microsoft-authentication.md) - [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) ## Overview This topic shows you how to register your apps to be able to use Azure Active Directory as an authentication provider for your mobile service. ## Registering your app >[AZURE.NOTE] The steps outlined in this topic are intended to be used with [Add Authentication to your Mobile Services app](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) tutorial when you want to use [Service-directed login operations](http://msdn.microsoft.com/library/azure/dn283952.aspx) with your app. Alternatively, if your app has a requirement for [client-directed login operations](http://msdn.microsoft.com/library/azure/jj710106.aspx) for Azure Active Directory and a .NET backend mobile service you should start with the [Authenticate your app with Active Directory Authentication Library Single Sign-On](mobile-services-windows-store-dotnet-adal-sso-authentication.md) tutorial. 1. Log on to the [Azure classic portal], navigate to your mobile service, click the **Identity** tab, then scroll down to the **Azure active directory** identity provider section and copy the **App URL** shown there. ![Mobile service app URL for AAD](./media/mobile-services-how-to-register-active-directory-authentication/mobile-services-copy-app-url-waad-auth.png) 2. Navigate to **Active Directory** in the [classic portal], click your directory then **Domains** and make a note of your directory's default domain. 3. Click **Applications** > **Add** > **Add an application my organization is developing**. 4. In the Add Application Wizard, enter a **Name** for your application and click the **Web application and/or Web API** type. ![Name your AAD app](./media/mobile-services-how-to-register-active-directory-authentication/mobile-services-add-app-wizard-1-waad-auth.png) 5. In the **Sign-on URL** box, paste the app URL value you copied from your mobile service. Enter the same unique value in the **App ID URI** box, then click to continue. ![Set the AAD app properties](./media/mobile-services-how-to-register-active-directory-authentication/mobile-services-add-app-wizard-2-waad-auth.png) 6. After the application has been added, click the **Configure** tab and copy the **Client ID** for the app. >[AZURE.NOTE]For a .NET backend mobile service, you must also edit the **Reply URL** value under **Single Sign-on** to be the URL of your mobile service appended with the path, _signin-aad_. For example, `https://todolist.azure-mobile.net/signin-aad` 7. Return to your mobile service's **Identity** tab and paste the copied **Client ID** value for the azure active directory identity provider. ![](./media/mobile-services-how-to-register-active-directory-authentication/mobile-services-clientid-pasted-waad-auth.png) 8. In the **Allowed Tenants** list, type the domain of the directory in which you registered the application (such as `contoso.onmicrosoft.com`), then click **Save**. You are now ready to use an Azure Active Directory for authentication in your app. [Azure classic portal]: https://manage.windowsazure.com/ [classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-how-to-register-facebook-authentication.md ================================================ # Register your apps for Facebook authentication with Mobile Services > [AZURE.SELECTOR] - [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md) - [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) - [Google](./ mobile-services-how-to-register-google-authentication.md) - [Microsoft account](./ mobile-services-how-to-register-microsoft-authentication.md) - [Twitter](./ mobile-services-how-to-register-twitter-authentication.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to register your apps to be able to use Facebook to authenticate with Azure Mobile Services. This page supports the [Get Started with Authentication](mobile-services-ios-get-started-users.md) tutorial which shows how to log users into your app. If this is your first experience with Mobile Services, please complete the tutorial [Get Started with Mobile Services](mobile-services-ios-get-started.md). To complete the procedure in this topic, you must have a Facebook account that has a verified email address and a mobile phone number. To create a new Facebook account, go to [facebook.com](http://go.microsoft.com/fwlink/p/?LinkId=268285). 1. Navigate to the [Facebook Developers](http://go.microsoft.com/fwlink/p/?LinkId=268285) website and sign-in with your Facebook account credentials. 2. (Optional) If you have not already registered, click **My Apps** then click **Register as a Developer**, accept the policy and follow the registration steps. 3. Click **My Apps** > **Add a New App** > **Website** > **Skip and Create App ID**. 4. In **Display Name**, enter a unique name for your app, choose a **Category** for you app, then click **Create App ID** and complete the security check. This takes you to the developer dashboard for your new Facebook app. 5. On the **App Secret** field, click **Show**, provide your password if requested, then make a note of the values of **App ID** and **App Secret**. You will uses these later to configure your application in Azure. > [AZURE.NOTE] **Security Note** The app secret is an important security credential. Do not share this secret with anyone or distribute it within a client application. 5. In the left navigation bar, click **Settings**, type the domain of your mobile service in **App Domains**, enter an optional **Contact Email**, click **Add Platform** and select **Website**. ![][3] 6. Type the URL of your mobile service in **Site URL**, then click **Save Changes**. 7. Click **Show**, provide your password if requested, then make a note of the values of **App ID** and **App Secret**. ![][5]   >[AZURE.IMPORTANT] The app secret is an important security credential. Do not share this secret with anyone or distribute it with your app.   8. Click the **Advanced** tab, type one of the following URL formats in **Valid OAuth redirect URIs**, then click **Save Changes**: + **.NET backend**: `https://.azure-mobile.net/signin-facebook` + **JavaScript backend**: `https://.azure-mobile.net/login/facebook` >[AZURE.NOTE]Make sure that you use the correct redirect URL path format for your type of Mobile Services backend, using the *HTTPS* scheme. When this is incorrect, authentication will not succeed. 12. The Facebook account which was used to register the application is an administrator of the app. At this point, only administrators can sign into this application. To authenticate other Facebook accounts, click **App Review** and enable **Make todolist-complete-nodejs public** to enable general public access using Facebook authentication. You are now ready to use a Facebook login for authentication in your app by providing the App ID and App Secret values to Mobile Services. [3]: ./media/mobile-services-how-to-register-facebook-authentication/mobile-services-facebook-configure-app.png [5]: ./media/mobile-services-how-to-register-facebook-authentication/mobile-services-facebook-completed.png [Facebook Developers]: http://go.microsoft.com/fwlink/p/?LinkId=268286 [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-dotnet/ [Azure Mobile Services]: http://azure.microsoft.com/services/mobile-services/ ================================================ FILE: docs/mobile-services-how-to-register-google-authentication.md ================================================ # Register your apps for Google login with Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md) - [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) - [Google](./ mobile-services-how-to-register-google-authentication.md) - [Microsoft account](./ mobile-services-how-to-register-microsoft-authentication.md) - [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) This topic shows you how to register your apps to be able to use Google to authenticate with Azure Mobile Services. >[AZURE.NOTE] This tutorial is about [Azure Mobile Services](https://azure.microsoft.com/services/mobile-services/), a solution to help you build scalable mobile applications for any platform. Mobile Services makes it easy to sync data, authenticate users, and send push notifications. This page supports the [Get Started with Authentication](mobile-services-ios-get-started-users.md) tutorial, which shows how to sign in users to your app.
If this is your first experience with Mobile Services, please complete the tutorial [Get Started with Mobile Services](mobile-services-ios-get-started.md). To complete the procedure in this topic, you must have a Google account that has a verified email address. To create a new Google account, go to accounts.google.com. 3. Navigate to the [Google apis](http://go.microsoft.com/fwlink/p/?LinkId=268303) website, sign-in with your Google account credentials, click **Create Project**, provide a **Project name**, then click **Create**. 4. In the **Products & services** drop down menu, click **API Manager**, then under **Social APIs** click **Google+ API** > **Enable API**. 5. Click **Credentials** > **OAuth consent screen**, then select your **Email address**, enter a **Product Name**, and click **Save**. 6. In the **Credentials** tab, click **Add credentials** > **OAuth 2.0 client ID**, then select **Web application**. 7. Type your mobile service URL in **Authorized JavaScript Origins**, replace the generated URL in **Authorized Redirect URI** with one of the following URL formats, and then click **Create**: + **.NET backend**: `https://.azure-mobile.net/signin-google` + **JavaScript backend**: `https://.azure-mobile.net/login/google` >[AZURE.NOTE]Make sure that you use the correct redirect URL path format for your type of Mobile Services backend. When this is incorrect, authentication will not succeed. 8. On the next screen, make a note of the values of the client ID and client secret. > [AZURE.IMPORTANT] The client secret is an important security credential. Do not share this secret with anyone or distribute it within a client application. You are now ready to configure your mobile service to use Google sign-in for authentication in your app. [Google apis]: http://go.microsoft.com/fwlink/p/?LinkId=268303 [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-dotnet/ ================================================ FILE: docs/mobile-services-how-to-register-microsoft-authentication.md ================================================ # Register your app to use Microsoft account for authentication >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md) - [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) - [Google](./ mobile-services-how-to-register-google-authentication.md) - [Microsoft account](./ mobile-services-how-to-register-microsoft-authentication.md) - [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) ## Overview This topic shows you how to register your mobile app to be able to use Microsoft account as an identity provider with Azure Mobile Services. The same steps apply for both service-directed authentication and client-directed authentication using the Live SDK. ## Register your Windows Store app at the Windows Dev Center Windows Store apps must first be registered with the Windows Dev Center. By registering, your Windows app will be able to use single sign-on behaviors. >[AZURE.NOTE]Windows Phone 8, Windows Phone 8.1 Silverlight, and non-Windows apps can skip this section. 1. If you have not already registered your app, navigate to the [Windows Dev Center](https://dev.windows.com/dashboard/Application/New), log on with your Microsoft account, type a name for your app, then click **Reserve app name**. 3. Open your Windows app project in Visual Studio, then in Solution Explorer right-click the Windows Store app project, click **Store** > **Associate App with the Store...**. ![](./media/mobile-services-how-to-register-microsoft-authentication/mobile-services-store-association.png) 5. In the wizard, click **Sign in** and sign-in with your Microsoft account, select the app name you reserved, then click **Next** > **Associate**. 6. (Optional) For a universal Windows 8.1 app, repeat steps 4 and 5 for the Windows Phone Store project. 6. Back in the Windows Dev Center page for your new app, click **Services** > **Push notifications**. 7. In the **Push notifications** page, click **Live Services site** under **Windows Push Notification Services (WNS) and Microsoft Azure Mobile Services**. This displays the Microsoft account app settings page for your app. 8. Make a note of the **Package SID** value. You can save this SID in the Azure portal to both enable single sign-on and push notifications for your Windows app. Next, you will configure Microsoft account authentication for your Windows app, starting with step 4 in the next section. ## Configure your Microsoft account registration and connect to Mobile Services If you have already registered your Windows app in the previous section, you can skip to step 2. 1. For a non-Windows Store app, navigate to the [My Applications](http://go.microsoft.com/fwlink/p/?LinkId=262039) page in the Microsoft account Developer Center, log on with your Microsoft account (if required), click **Create application**, type an **Application name**, then click **I accept**. This reserves you app name with Microsoft account and displays the Microsoft account page for your app. 2. In the Microsoft account page for your app, click **API Settings**, enable **Mobile or desktop client app**, set the mobile service URL as the **Target domain**, then supply one of the following URL formats in **Redirect URL** and click **Save**: + **.NET backend**: `https://.azure-mobile.net/signin-microsoft` + **JavaScript backend**: `https://.azure-mobile.net/login/microsoftaccount` >[AZURE.NOTE]Make sure that you use the correct redirect URL path format for your type of Mobile Services backend. When this is incorrect, authentication will not succeed. The **Root domain** should fill in automatically.   ![Microsoft account API settings](./media/mobile-services-how-to-register-microsoft-authentication/mobile-services-win8-app-push-auth-2.png) 4. Click **App Settings** and make a note of the values of the **Client ID**, **Client secret** and **Package SID**. ![Microsoft account app settings](./media/mobile-services-how-to-register-microsoft-authentication/mobile-services-win8-app-push-auth.png) > [AZURE.NOTE] The client secret is an important security credential. Do not share the client secret with anyone or distribute it with your app. Only Windows Store app registrations will see a Package SID field. 4. In the [Azure classic portal], click the **Identity** tab for the mobile service, enter the client ID, client secret and package SID obtained from your identity provider, then click **Save**. >[AZURE.NOTE]You do not need to supply a Package SID value for a Windows Phone 8, Windows Phone Store 8.1 Silverlight, or a non-Windows app. Both your mobile service and your app are now configured to work with Microsoft account. [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-how-to-register-twitter-authentication.md ================================================ # Register your apps for Twitter login with Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md) - [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) - [Google](./ mobile-services-how-to-register-google-authentication.md) - [Microsoft account](./ mobile-services-how-to-register-microsoft-authentication.md) - [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) This topic shows you how to register your apps to be able to use Twitter to authenticate with Azure Mobile Services. >[AZURE.NOTE] This tutorial is about [Azure Mobile Services](https://azure.microsoft.com/services/mobile-services/), a solution to help you build scalable mobile applications for any platform. Mobile Services makes it easy to sync data, authenticate users, and send push notifications. This page supports the [Add authentication to your app](mobile-services-ios-get-started-users.md) tutorial which shows how to sign users into your app. If this is your first experience with Mobile Services, please complete the tutorial [Get Started with Mobile Services](mobile-services-ios-get-started.md). To complete the procedure in this topic, you must have a Twitter account that has a verified email address. To create a new Twitter account, go to twitter.com. 1. Navigate to the [Twitter Developers](http://go.microsoft.com/fwlink/p/?LinkId=268300) website, sign-in with your Twitter account credentials, and click **Create new app**. 2. Type the **Name**, **Description**, and **Website** values for your app, then type one of the following URL formats in **Callback URL**. + **.NET backend**: `https://.azure-mobile.net/signin-twitter` + **JavaScript backend**: `https://.azure-mobile.net/login/twitter` >[AZURE.NOTE]Make sure that you use the correct redirect URL path format for your type of Mobile Services backend. When this is incorrect, authentication will not succeed.   ![][2] 3. At the bottom the page, read and accept the terms, and then click **Create your Twitter application**. This registers the app displays the application details. 6. Click the **Keys and Access Tokens** tab in your app dashboard and make a note of the values of **Consumer key** and **Consumer secret**. > [AZURE.NOTE] The consumer secret is an important security credential. Do not share this secret with anyone or distribute it with your app. 7. Click the **Settings** tab, scroll down and make sure the **Allow this application to be used to sign in with Twitter** checkbox is checked, then click **Update Settings**. You are now ready to use a Twitter login for authentication in your app by providing the consumer key and consumer secret values to Mobile Services. [1]: ./media/mobile-services-how-to-register-twitter-authentication/mobile-services-twitter-developers.png [2]: ./media/mobile-services-how-to-register-twitter-authentication/mobile-services-twitter-register-app1.png [Twitter Developers]: http://go.microsoft.com/fwlink/p/?LinkId=268300 [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-dotnet/ ================================================ FILE: docs/mobile-services-how-to-use-multiple-clients-single-service.md ================================================ # Supporting multiple device platforms from a single mobile service >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   One of the major benefits of using Azure Mobile Services in your mobile app development is the ability to use a single backend service that supports your app on multiple client platforms. Mobile Services provides native client libraries for all major device platforms, which makes it easier to develop apps using a single backend service and by using cross-platform developer tools. This topic discusses considerations for getting your app running on multiple client platforms while using a single mobile service backend. ## Cross-platform push notifications Mobile Services uses Azure Notification Hubs for sending push notifications to your client apps on all major device platforms. Notification Hubs provide a consistent and unified infrastructure for creating and managing device registrations and for sending cross-platform push notifications. Notification Hubs supports sending push notifications by using the following platform-specific notification services: + Apple Push Notification Service (APNS) for iOS apps + Google Cloud Messaging (GCM) service for Android apps + Windows Notification Service (WNS) for Windows Store, Windows Phone 8.1 Store, and universal Windows apps + Microsoft Push Notification Service (MPNS) for Windows Phone Silverlight apps For more information, see [Azure Notification Hubs]. Client registrations are created by using the register function in the platform-specific Mobile Services client library or by using the Mobile Services REST APIs. Notification Hubs supports two kinds of device registrations: + **Native registration**
Native registrations are tailored to the platform-specific push notification service. When sending notifications to devices registered using native registrations, you must call platform-specific APIs in your mobile service. To send a notification to devices on multiple platforms requires multiple platform-specific calls. + **Template registration**
Notification Hubs also supports platform-specific template registrations. By using template registrations, you can use a single API call to send a notification to your app running on any registered platform. For more information, see [Send cross-platform notifications to users]. Tables in the following sections link to the client-specific tutorials that show you how to implement push notifications from both .NET and JavaScript backend mobile services. ### .NET backend In a .NET backend mobile service, you send notifications by calling the [SendAsync] method on the [PushClient](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.notifications.pushclient.aspx) object obtained from the [ApiServices.Push](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.apiservices.push.aspx) property. The push notification sent (native or template) depends on the specific [IPushMessage](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.notifications.ipushmessage.aspx)-derived object that is passed to the [SendAsync] method, as shown in the following table: |Platform |[APNS](mobile-services-dotnet-backend-ios-get-started-push.md)|[GCM](mobile-services-dotnet-backend-android-get-started-push.md) |[WNS](mobile-services-dotnet-backend-windows-store-dotnet-get-started-push.md) | MPNS |-----|-----|----|----|-----| |Native|[ApplePushMessage](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.applepushmessage.aspx) |[GooglePushMessage](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.googlepushmessage.aspx) |[WindowsPushMessage](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.windowspushmessage.aspx) | [MpnsPushMessage](http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.mpnspushmessage.aspx) | The following code sends a push notification from a .NET backend service to all iOS and Windows Store device registrations: // Define a push notification for APNS. ApplePushMessage apnsMessage = new ApplePushMessage(item.Text, TimeSpan.FromHours(1)); // Define a push notification for WNS. WindowsPushMessage wnsMessage = new WindowsPushMessage(); wnsMessage.XmlPayload = @"" + @"" + @"" + item.Text + @"" + @""; // Send push notifications to all registered iOS and Windows Store devices. await Services.Push.SendAsync(apnsMessage); await Services.Push.SendAsync(wnsMessage); For examples of how to send push notifications to the other native client platforms, click the platform links in the header of the above table. When you use template client registrations rather than native client registrations, you can send the same notification with only a single call to [SendAsync], supplying a [TemplatePushMessage] object, as follows: // Create a new template message and add the 'message' parameter. var templatePayload = new TemplatePushMessage(); templatePayload.Add("message", item.Text); // Send a push notification to all template registrations. await Services.Push.SendAsync(templatePayload); ### JavaScript backend In a JavaScript backend mobile service, you send notifications by calling the **send** method on the platform-specific object obtained from the global [push object], as shown in the following table: |Platform |[APNS](mobile-services-javascript-backend-ios-get-started-push.md)|[GCM](mobile-services-javascript-backend-android-get-started-push.md) |[WNS](mobile-services-javascript-backend-windows-store-dotnet-get-started-push.md) |[MPNS](mobile-services-javascript-backend-windows-phone-get-started-push.md)| |-----|-----|----|----|-----| |Native|[apns object](http://msdn.microsoft.com/library/azure/jj839711.aspx) |[gcm object](http://msdn.microsoft.com/library/azure/dn126137.aspx) |[wns object](http://msdn.microsoft.com/library/azure/jj860484.aspx) | [mpns object](http://msdn.microsoft.com/library/azure/jj871025.aspx) | The following code sends push notification to all Android and Windows Phone registrations: // Define a push notification for GCM. var gcmPayload = '{"data":{"message" : item.text }}'; // Define the payload for a Windows Phone toast notification. var mpnsPayload = '' + '' + 'New Item' + item.text + ''; // Send push notifications to all registered Android and Windows Phone 8.0 devices. push.mpns.send(null, mpnsPayload, 'toast', 22, { success: function(pushResponse) { // Push succeeds. }, error: function (pushResponse) { // Push fails. } }); push.gcm.send(null, gcmPayload, { success: function(pushResponse) { // Push succeeds. }, error: function (pushResponse) { // Push fails. } }); For examples of how to send push notifications to the other native client platforms, click the platform links in the header of the above table. When you use template client registrations rather than native client registrations, you can send the same notification with only a single call the **send** function on the global [push object], supplying a template message payload, as follows: // Create a new template message with the 'message' parameter. var templatePayload = { "message": item.text }; // Send a push notification to all template registrations. push.send(null, templatePayload, { success: function(pushResponse) { // Push succeeds. }, error: function (pushResponse) { // Push fails. } }); ## Cross-platform app development Developing native mobile device apps for all of the major mobile device platforms requires you (or your organization) to have expertise in at least Objective-C, Java, and C# or JavaScript programming languages. Because of the expense of developing across these disparate platforms, some developers choose a fully web browser-based experience for their apps. However, such web-based experiences cannot access most of the native resources that provide the rich experience that users have come to expect on their mobile devices. Cross-platform tools are available that provide a richer native experience on a mobile device, while still sharing a single code base, usually JavaScript. Mobile Services makes it easy to create and manage a backend service for cross-platform app development platforms by providing quickstart tutorials for the following development platforms: + [**PhoneGap**](https://go.microsoft.com/fwLink/p/?LinkID=390707)**/**[**Cordova**](http://cordova.apache.org/)
PhoneGap (a distribution of the Apache Cordova project) is a free and open source framework that lets you use standardized web APIs, HTML and JavaScript to develop a single app that runs on Android, iOS and Windows devices. PhoneGap provides a web view based UI, but with a user experience enhanced by accessing native resources on the device, such as such as push notifications, the accelerometer, camera, storage, geolocation, and the in-app browser. For more information, see the [PhoneGap quickstart tutorial][PhoneGap]. Visual Studio now also enables you to build cross-platform Cordova apps by using the Multi-Device Hybrid Apps extension for Visual Studio, which is pre-release software. For more information, see [Getting Started with Multi-Device Hybrid Apps Using HTML and JavaScript](http://msdn.microsoft.com/library/dn771545.aspx). + [**Sencha Touch**](http://go.microsoft.com/fwlink/p/?LinkId=509988)
Sencha Touch provides a set of controls, optimized for touch screens, that provide a like-native experience on a wide variety of mobile devices from a single HTML and JavaScript code base. Sencha Touch can be used along with PhoneGap or Cordova libraries to provide users access to native device resources. For more information, see the [Sencha Touch quickstart tutorial][Sencha]. + [**Xamarin**](https://go.microsoft.com/fwLink/p/?LinkID=330242)
Xamarin lets you create fully native apps for both iOS and Android devices, with fully native UI and access to all device resources. Xamarin apps are coded in C# instead of Objective-C and Java. This enables .NET developers to publish apps to iOS and Android and share code from Windows projects. Xamarin provides a fully native user experience on both iOS and Android devices from C# code. This enables you to reuse some of your Mobile Services code from Windows apps on iOS and Android devices. For more information, see [Xamarin development](#xamarin) below. [Azure Notification Hubs]: /develop/net/how-to-guides/service-bus-notification-hubs/ [SSO Windows Store]: https://azure.microsoft.com/develop/mobile/tutorials/single-sign-on-windows-8-dotnet/ [SSO Windows Phone]: https://azure.microsoft.com/develop/mobile/tutorials/single-sign-on-wp8/ [Tutorials and resources]: https://azure.microsoft.com/develop/mobile/resources/ [Get started with Notification Hubs]: /manage/services/notification-hubs/getting-started-windows-dotnet/ [Send cross-platform notifications to users]: /manage/services/notification-hubs/notify-users-xplat-mobile-services/ [Get started with push Windows dotnet]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-dotnet-vs2012/ [Get started with push Windows js]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-js-vs2012/ [Get started with push Windows Phone]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-wp8/ [Get started with push iOS]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-ios/ [Get started with push Android]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-android/ [Dynamic schema]: http://msdn.microsoft.com/library/windowsazure/jj193175.aspx [How to use a .NET client with Mobile Services]: documentation/articles/mobile-services-windows-dotnet-how-to-use-client-library/ [push object]: http://msdn.microsoft.com/library/windowsazure/jj554217.aspx [TemplatePushMessage]:http://msdn.microsoft.com/library/azure/microsoft.windowsazure.mobile.service.templatepushmessage.aspx [PhoneGap]: mobile-services-javascript-backend-phonegap-get-started.md [Sencha]: partner-sencha-mobile-services-get-started.md [Appcelerator]: partner-appcelerator-mobile-services-javascript-backend-appcelerator-get-started.md [SendAsync]: http://msdn.microsoft.com/library/microsoft.windowsazure.mobile.service.notifications.pushclient.sendasync.aspx [What's next for Windows Phone 8 developers]: http://msdn.microsoft.com/library/windows/apps/dn655121(v=vs.105).aspx [Building universal Windows apps for all Windows devices]: http://go.microsoft.com/fwlink/p/?LinkId=509905 [Universal Windows app project for Azure Mobile Services using MVVM]: http://code.msdn.microsoft.com/Universal-Windows-app-for-db3564de ================================================ FILE: docs/mobile-services-how-to-use-server-scripts.md ================================================ # Work with a JavaScript backend mobile service This article provides detailed information about and examples of how to work with a JavaScript backend in Azure Mobile Services. ## Introduction In a JavaScript backend mobile service, you can define custom business logic as JavaScript code that's stored and executed on the server. This server script code is assigned to one of the following server functions: + [Insert, read, update, or delete operations on a given table][Table operations]. + [Scheduled jobs][Job Scheduler]. + [HTTP methods defined in a custom API][Custom API anchor]. The signature of the main function in the server script depends on the context of where the script is used. You can also define common script code as nodes.js modules that are shared across scripts. For more information, see [Source control and shared code][Source control, shared code, and helper functions]. For descriptions of individual server script objects and functions, see [Mobile Services server script reference]. ## Table operations A table operation script is a server script that is registered to an operation on a table—insert, read, update, or delete (*del*). This section describes how to work with table operations in a JavaScript backend, which includes the following sections: + [Overview of table operations][Basic table operations] + [How to: Register for table operations] + [How to: Override the default response] + [How to: Override execute success] + [How to: Override default error handling] + [How to: Generate unique ID values](#generate-guids) + [How to: Add custom parameters] + [How to: Work with table users][How to: Work with users] ### Overview of table operations The name of the script must match the kind of operation for which it is registered. Only one script can be registered for a given table operation. The script is executed every time that the given operation is invoked by a REST request—for example, when a POST request is received to insert an item into the table. Mobile Services does not preserve state between script executions. Because a new global context is created every time a script is run, any state variables that are defined in the script are reinitialized. If you want to store state from one request to another, create a table in your mobile service, and then read and write the state to the table. For more information, see [How to: Access tables from scripts]. You write table operation scripts if you need to enforce customized business logic when the operation is executed. For example, the following script rejects insert operations where the string length of the `text` field is greater than ten characters: function insert(item, user, request) { if (item.text.length > 10) { request.respond(statusCodes.BAD_REQUEST, 'Text length must be less than 10 characters'); } else { request.execute(); } } A table script function always takes three arguments. - The first argument varies depending on the table operation. - For inserts and updates, it is an **item** object, which is a JSON representation of the row being affected by the operation. This allows you to access column values by name, for example, *item.Owner*, where *Owner* is one of the names in the JSON representation. - For a delete, it is the ID of the record to delete. - And for a read, it is a [query object] that specifies the rowset to return. - The second argument is always a [user object][User object] that represents the user that submitted the request. - The third argument is always a [request object][request object], by which you can control execution of the requested operation and the response that's sent to the client. Here are the canonical main-function signatures for the table operations: + [Insert][insert function]: `function insert (item, user, request) { ... }` + [Update][update function]: `function update (item, user, request) { ... }` + [Delete][delete function]: `function del (id, user, request) { ... }` + [Read][read function]: `function read (query, user, request) { ... }` >[AZURE.NOTE]A function that's registered to the delete operation must be named _del_ because delete is a reserved keyword in JavaScript. Every server script has a main function, and may have optional helper functions. Even though a server script may have been created for a specific table, it can also reference other tables in the same database. You can also define common functions as modules that can be shared across scripts. For more information, see [Source control and shared code][Source control, shared code, and helper functions]. ### How to: Register table scripts You can define server scripts that are registered to a table operation in one of the following ways: + In the [Azure classic portal]. Scripts for table operations are accessed in the **Scripts** tab for a given table. The following shows the default code registered to the insert script for the `TodoItem` table. You can override this code with your own custom business logic. ![1][1] To learn how to do this, see [Validate and modify data in Mobile Services by using server scripts]. + By using source control. When you have source control enabled, simply create a file named ``.``.js in the .\service\table subfolder in your git repository, where `
` is the name of the table and `` is the table operation being registered. For more information, see [Source control and shared code][Source control, shared code, and helper functions]. + From the command prompt by using the Azure command line tool. For more information, see [Using the command line tool]. A table operation script must call at least one of the following functions of the [request object] to make sure that the client receives a response. + **execute function**: The operation is completed as requested and the standard response is returned. + **respond function**: A custom response is returned. > [AZURE.IMPORTANT] When a script has a code path in which neither **execute** nor **respond** is invoked, the operation may become unresponsive. The following script calls the **execute** function to complete the data operation requested by the client: function insert(item, user, request) { request.execute(); } In this example, the item is inserted into the database and the appropriate status code is returned to the user. When the **execute** function is called, the `item`, [query][query object], or `id` value that was passed as the first argument into the script function is used to perform the operation. For an insert, update or query operation, you can modify the item or query before you call **execute**: function insert(item, user, request) { item.scriptComment = 'this was added by a script and will be saved to the database'; request.execute(); } function update(item, user, request) { item.scriptComment = 'this was added by a script and will be saved to the database'; request.execute(); } function read(query, user, request) { // Only return records for the current user query.where({ userid: user.userId}); request.execute(); } >[AZURE.NOTE]In a delete script, changing the value of the supplied userId variable does not affect which record gets deleted. For more examples, see [Read and write data], [Modify the request] and [Validate data]. ### How to: Override the default response You can also use a script to implement validation logic that can override the default response behavior. If validation fails, just call the **respond** function instead of the **execute** function and write the response to the client: function insert(item, user, request) { if (item.userId !== user.userId) { request.respond(statusCodes.FORBIDDEN, 'You may only insert records with your userId.'); } else { request.execute(); } } In this example, the request is rejected when the inserted item does not have a `userId` property that matches the `userId` of the [user object] that's supplied for the authenticated client. In this case, a database operation (*insert*) does not occur, and a response that has a 403 HTTP status code and a custom error message is returned to the client. For more examples, see [Modify the response]. ### How to: Override execute success By default in a table operation, the **execute** function writes responses automatically. However, you can pass two optional parameters to the execute function that override its behavior on success and/or on error. By passing in a **success** handler when you call execute, you can modify the results of a query before you write them to the response. The following example calls `execute({ success: function(results) { ... })` to perform additional work after data is read from the database but before the response is written: function read(query, user, request) { request.execute({ success: function(results) { results.forEach(function(r) { r.scriptComment = 'this was added by a script after querying the database'; }); request.respond(); } }); } When you provide a **success** handler to the **execute** function, you must also call the **respond** function as part of the **success** handler so that the runtime knows that the script has completed and that a response can be written. When you call **respond** without passing any arguments, Mobile Services generates the default response. >[AZURE.NOTE]You can call **respond** without arguments to invoke the default response only after you first call the **execute** function. ### How to: Override default error handling The **execute** function can fail if there is a loss of connectivity to the database, an invalid object, or an incorrect query. By default when an error occurs, server scripts log the error and write an error result to the response. Because Mobile Services provides default error handling, you don't have to handle errors that may occur in the service. You can override the default error handling by implementing explicit error handling if you want a particular compensating action or when you want to use the global console object to write more detailed information to the log. Do this by supplying an **error** handler to the **execute** function: function update(item, user, request) { request.execute({ error: function(err) { // Do some custom logging, then call respond. request.respond(); } }); } When you provide an error handler, Mobile Services returns an error result to the client when **respond** is called. You can also provide both a **success** and an **error** handler if you wish. ### How to: Generate unique ID values Mobile Services supports unique custom string values for the table's **id** column. This allows applications to use custom values such as email addresses or user names for the ID. String IDs provide you with the following benefits: + IDs are generated without making a round-trip to the database. + Records are easier to merge from different tables or databases. + IDs values can integrate better with an application's logic. When a string ID value is not set on an inserted records, Mobile Services generates a unique value for the ID. You can generate your own unique ID values in server scripts. The script example below generates a custom GUID and assigns it to a new record's ID. This is similar to the id value that Mobile Services would generate if you didn't pass in a value for a record's ID. // Example of generating an id. This is not required since Mobile Services // will generate an id if one is not passed in. item.id = item.id || newGuid(); request.execute(); function newGuid() { var pad4 = function(str) { return "0000".substring(str.length) + str; }; var hex4 = function () { return pad4(Math.floor(Math.random() * 0x10000 /* 65536 */ ).toString(16)); }; return (hex4() + hex4() + "-" + hex4() + "-" + hex4() + "-" + hex4() + "-" + hex4() + hex4() + hex4()); } When an application provides a value for an ID, Mobile Services stores it as-is. This includes leading or trailing white spaces. White space are not trimmed from value. The value for the `id` must be unique and it must not include characters from the following sets: + Control characters: [0x0000-0x001F] and [0x007F-0x009F]. For more information, see [ASCII control codes C0 and C1](http://en.wikipedia.org/wiki/Data_link_escape_character#C1_set). + Printable characters: **"**(0x0022), **\+** (0x002B), **/** (0x002F), **?** (0x003F), **\\** (0x005C), **`** (0x0060) + The ids "." and ".." You can also use integer IDs for your tables. To use an integer ID, you must create your table with the `mobile table create` command using the `--integerId` option. This command is used with the Command-line Interface (CLI) for Azure. For more information on using the CLI, see [CLI to manage Mobile Services tables](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Mobile_Tables). ### How to: Access custom parameters When you send a request to your mobile service, you can include custom parameters in the URI of the request to instruct your table operation scripts how to process a given request. You then modify your script to inspect the parameter to determine the processing path. For example, the following URI for a POST request tells the service to not permit the insertion of a new *TodoItem* that has the same text value: https://todolist.azure-mobile.net/tables/TodoItem?duplicateText=false These custom query parameters are accessed as JSON values from the **parameters** property of the [request object]. The **request** object is supplied by Mobile Services to any function registered to a table operation. The following server script for the insert operation checks the value of the `duplicateText` parameter before the insert operation is run: function insert(item, user, request) { var todoItemTable = tables.getTable('TodoItem'); // Check the supplied custom parameter to see if // we should allow duplicate text items to be inserted. if (request.parameters.duplicateText === 'false') { // Find all existing items with the same text // and that are not marked 'complete'. todoItemTable.where({ text: item.text, complete: false }).read({ success: insertItemIfNotComplete }); } else { request.execute(); } function insertItemIfNotComplete(existingItems) { if (existingItems.length > 0) { request.respond(statusCodes.CONFLICT, "Duplicate items are not allowed."); } else { // Insert the item as normal. request.execute(); } } } Note that in **insertItemIfNotComplete** the **execute** function of the [request object] is invoked to insert the item when there is no duplicate text; otherwise the **respond** function is invoked to notify the client of the duplicate. Note the syntax of the call to the **success** function in the above code: }).read({ success: insertItemIfNotComplete }); In JavaScript it is a compact version of the lengthier equivalent: success: function(results) { insertItemIfNotComplete(results); } ### How to: Work with users In Azure Mobile Services, you can use an identity provider to authenticate users. For more information, see [Get started with authentication]. When an authenticated user invokes a table operation, Mobile Services uses the [user object] to supply information about the user to the registered script function. The **userId** property can be used to store and retrieve user-specific information. The following example sets the owner property of an item based on the **userId** of an authenticated user: function insert(item, user, request) { item.owner = user.userId; request.execute(); } The next example adds an additional filter to the query based on the **userId** of an authenticated user. This filter restricts the result to only items that belong to the current user: function read(query, user, request) { query.where({ owner: user.userId }); request.execute(); } ## Custom APIs This section describes how you create and work with custom API endpoints, which includes the following sections: + [Overview of custom APIs](#custom-api-overview) + [How to: Define a custom API] + [How to: Implement HTTP methods] + [How to: Send and receive data as XML] + [How to: Work with users and headers in a custom API] + [How to: Define multiple routes in a custom API] ### Overview of custom APIs A custom API is an endpoint in your mobile service that is accessed by one or more of the standard HTTP methods: GET, POST, PUT, PATCH, DELETE. A separate function export can be defined for each HTTP method supported by the custom API, all in a single script file. The registered script is invoked when a request to the custom API using the given method is received. For more information, see [Custom API]. When custom API functions are called by the Mobile Services runtime, both a [request][request object] and [response][response object] object are supplied. These objects expose the functionality of the [express.js library], which can be leveraged by your scripts. The following custom API named **hello** is a very simple example that returns _Hello, world!_ in response to a POST request: exports.post = function(request, response) { response.send(200, "{ message: 'Hello, world!' }"); } The **send** function on the [response object] returns your desired response to the client. This code is invoked by sending a POST request to the following URL: https://todolist.azure-mobile.net/api/hello The global state is maintained between executions. ### How to: Define a custom API You can define server scripts that are registered to HTTP methods in a custom API endpoint in one of the following ways: + In the [Azure classic portal]. Custom API scripts are created and modified in the **API** tab. The server script code is in the **Scripts** tab of a given custom API. The following shows the script that is invoked by a POST request to the `CompleteAll` custom API endpoint. ![2][2] Access permissions to custom API methods are assigned in the Permissions tab. To see how this custom API was created, see [Call a custom API from the client]. + By using source control. When you have source control enabled, simply create a file named ``.js in the .\service\api subfolder in your git repository, where `` is the name of the custom API being registered. This script file contains an _exported_ function for each HTTP method exposed by the custom API. Permissions are defined in a companion .json file. For more information, see [Source control and shared code][Source control, shared code, and helper functions]. + From the command prompt by using the Azure command line tool. For more information, see [Using the command line tool]. ### How to: Implement HTTP methods A custom API can handle one or more of the HTTP methods, GET, POST, PUT, PATCH, and DELETE. An exported function is defined for each HTTP method handled by the custom API. A single custom API code file can export one or all of the following functions: exports.get = function(request, response) { ... }; exports.post = function(request, response) { ... }; exports.patch = function(request, response) { ... }; exports.put = function(request, response) { ... }; exports.delete = function(request, response) { ... }; The custom API endpoint cannot be called using an HTTP method that has not been implemented in the server script, and a 405 (Method Not Allowed) error response is returned. Separate permission levels can be assigned to each support HTTP method. ### How to: Send and receive data as XML When clients store and retrieve data, Mobile Services uses JavaScript Object Notation (JSON) to represent data in the message body. However, there are scenarios where you instead want to use an XML payload. For example, Windows Store apps have a built-in periodic notifications functionality that requires the service to emit XML. For more information, see [Define a custom API that supports periodic notifications]. The following **OrderPizza** custom API function returns a simple XML document as the response payload: exports.get = function(request, response) { response.set('content-type', 'application/xml'); var xml = ''; response.send(200, xml); }; This custom API function is invoked by an HTTP GET request to the following endpoint: https://todolist.azure-mobile.net/api/orderpizza ### How to: Work with users and headers in a custom API In Azure Mobile Services, you can use an identity provider to authenticate users. For more information, see [Get started with authentication]. When an authenticated user requests a custom API, Mobile Services uses the [user object] to provide information about the user to custom API code. The [user object] is accessed from the user property of the [request object]. The **userId** property can be used to store and retrieve user-specific information. The following **OrderPizza** custom API function sets the owner property of an item based on the **userId** of an authenticated user: exports.post = function(request, response) { var userTable = request.service.tables.getTable('user'); userTable.lookup(request.user.userId, { success: function(userRecord) { callPizzaAPI(userRecord, request.body, function(orderResult) { response.send(201, orderResult); }); } }); }; This custom API function is invoked by an HTTP POST request to the following endpoint: https://.azure-mobile.net/api/orderpizza You can also access a specific HTTP header from the [request object], as shown in the following code: exports.get = function(request, response) { var header = request.header('my-custom-header'); response.send(200, "You sent: " + header); }; This simple example reads a custom header named `my-custom-header`, then returns the value in the response. ### How to: Define multiple routes in a custom API Mobile Services enables you to define multiple paths, or routes, in a custom API. For example, HTTP GET requests to the following URLs in a **calculator** custom API will invoke an **add** or **subtract** function, respectively: + `https://.azure-mobile.net/api/calculator/add` + `https://.azure-mobile.net/api/calculator/sub` Multiple routes are defined by exporting a **register** function, which is passed an **api** object (similar to the [express object in express.js]) that is used to register routes under the custom API endpoint. The following example implements the **add** and **sub** methods in the **calculator** custom API: exports.register = function (api) { api.get('add', add); api.get('sub', subtract); } function add(req, res) { var result = parseInt(req.query.a) + parseInt(req.query.b); res.send(200, { result: result }); } function subtract(req, res) { var result = parseInt(req.query.a) - parseInt(req.query.b); res.send(200, { result: result }); } The **api** object passed to the **register** function exposes a function for each HTTP method (**get**, **post**, **put**, **patch**, **delete**). These functions register a route to a defined function for a specific HTTP method. Each function takes two parameters, the first is the route name and the second is the function registered to the route. The two routes in the above custom API example can be invoked by HTTP GET requests as follows (shown with the response): + `https://.azure-mobile.net/api/calculator/add?a=1&b=2` {"result":3} + `https://.azure-mobile.net/api/calculator/sub?a=3&b=5` {"result":-2} ## Job Scheduler Mobile Services enables you to define server scripts that are executed either as jobs on a fixed schedule or on-demand from the Azure classic portal. Scheduled jobs are useful for performing periodic tasks such as cleaning-up table data and batch processing. For more information, see [Schedule jobs]. Scripts that are registered to scheduled jobs have a main function with the same name as the scheduled job. Because a scheduled script is not invoked by an HTTP request, there is no context that can be passed by the server runtime and the function takes no parameters. Like other kinds of scripts, you can have subroutine functions and require shared modules. For more information, see [Source control, shared code, and helper functions]. ### How to: Define scheduled job scripts A server script can be assigned to a job that's defined in the Mobile Services Scheduler. These scripts belong to the job and are executed according to the job schedule. (You can also use the [Azure classic portal] to run jobs on demand.) A script that defines a scheduled job has no parameters because Mobile Services doesn't pass it any data; it's executed as a regular JavaScript function and doesn't interact with Mobile Services directly. You define scheduled jobs in one of the following ways: + In the [Azure classic portal] in the **Script** tab in the scheduler: ![3][3] For more information about how to do this, see [Schedule backend jobs in Mobile Services]. + From the command prompt by using the Azure command line tool. For more information, see [Using the command line tool]. >[AZURE.NOTE]When you have source control enabled, you can edit scheduled job script files directly in the .\service\scheduler subfolder in your git repository. For more information, see [How to: Share code by using source control]. ## Source control, shared code, and helper functions This sections shows you how to leverage source control to add your own custom node.js modules, shared code and other code reuse strategies, including the following sections: + [Overview of leveraging shared code](#leverage-source-control) + [How to: Load Node.js modules] + [How to: Use helper functions] + [How to: Share code by using source control] + [How to: Work with app settings] ### Overview of leveraging shared code Because Mobile Services uses Node.js on the server, your scripts already have access to the built-in Node.js modules. You can also use source control to define your own modules or add other Node.js modules to your service. The following are just some of the more useful modules that can be leveraged in your scripts by using the global **require** function: + **azure**: Exposes the functionality of the Azure SDK for Node.js. For more information, see the [Azure SDK for Node.js]. + **crypto**: Provides crypto functionality of OpenSSL. For more information, see the [Node.js documentation][crypto API]. + **path**: Contains utilities for working with file paths. For more information, see the [Node.js documentation][path API]. + **querystring**: Contains utilities for working with query strings. For more information, see the [Node.js documentation][querystring API]. + **request**: Sends HTTP requests to external REST services, such as Twitter and Facebook. For more information, see [Send HTTP request]. + **sendgrid**: Sends email by using the Sendgrid email service in Azure. For more information, see [Send email from Mobile Services with SendGrid]. + **url**: Contains utilities to parse and resolve URLs. For more information, see the [Node.js documentation][url API]. + **util**: Contains various utilities, such as string formatting and object type checking. For more information, see the [Node.js documentation][util API]. + **zlib**: Exposes compression functionality, such as gzip and deflate. For more information, see the [Node.js documentation][zlib API]. ### How to: Leverage modules Mobile Services exposes a set of modules that scripts can load by using the global **require** function. For example, a script can require **request** to make HTTP requests: function update(item, user, request) { var httpRequest = require('request'); httpRequest('http://www.google.com', function(err, response, body) { ... }); } ### How to: Share code by using source control You can use source control with the Node.js package manager (npm) to control which modules are available to your mobile service. There are two ways to do this: + For modules that are published to and installed by npm, use the package.json file to declare which packages you want to be installed by your mobile service. In this way, your service always has access to the latest version of the required packages. The package.json file lives in the `.\service` directory. For more information, see [Support for package.json in Azure Mobile Services]. + For private or custom modules, you can use npm to manually install the module into the `.\service\node_modules` directory of your source control. For an example of how to manually upload a module, see [Leverage shared code and Node.js modules in your server scripts]. >[AZURE.NOTE]When `node_modules` already exists in the directory hierarchy, NPM will create the `\node-uuid` subdirectory there instead of creating a new `node_modules` in the repository. In this case, just delete the existing `node_modules` directory. After you commit the package.json file or custom modules to the repository for your mobile service, use **require** to reference the modules by name. >[AZURE.NOTE] Modules that you specify in package.json or upload to your mobile service are only used in your server script code. These modules are not used by the Mobile Services runtime. ### How to: Use helper functions In addition to requiring modules, individual server scripts can include helper functions. These are functions that are separate from the main function, which can be used to factor code in the script. In the following example, a table script is registered to the insert operation, which includes the helper function **handleUnapprovedItem**: function insert(item, user, request) { if (!item.approved) { handleUnapprovedItem(item, user, request); } else { request.execute(); } } function handleUnapprovedItem(item, user, request) { // Do something with the supplied item, user, or request objects. } In a script, helper functions must be declared after the main function. You must declare all variables in your script. Undeclared variables cause an error. Helper functions can also be defined once and shared between server scripts. To share a function between scripts, functions must be exported and the script file must exist in the `.\service\shared\` directory. The following is a template for how to export a shared function in a file `.\services\shared\helpers.js`: exports.handleUnapprovedItem = function (tables, user, callback) { // Do something with the supplied tables or user objects and // return a value to the callback function. }; You can then use a function like this in a table operation script: function insert(item, user, request) { var helper = require('../shared/helper'); helper.handleUnapprovedItem(tables, user, function(result) { // Do something based on the result. request.execute(); } } } In this example, you must pass both a [tables object] and a [user object] to the shared function. This is because shared scripts cannot access the global [tables object], and the [user object] only exists in the context of a request. Script files are uploaded to the shared directory either by using [source control][How to: Share code by using source control] or by using the [command line tool][Using the command line tool]. ### How to: Work with app settings Mobile Services enables you to securely store values as app settings, which can be accessed by your server scripts at runtime. When you add data to the app settings of your mobile service, the name/value pairs are stored encrypted and you can access them in your server scripts without hard-coding them in the script file. For more information, see [App settings]. The following custom API example uses the supplied [service object] to retrieve an app setting value. exports.get = function(request, response) { // Get the MY_CUSTOM_SETTING value from app settings. var customSetting = request.service.config.appSettings.my_custom_setting; // Do something and then send a response. } The following code uses the configuration module to retrieve Twitter access token values, stored in app settings, that are used in a scheduled job script: // Get the service configuration module. var config = require('mobileservice-config'); // Get the stored Twitter consumer key and secret. var consumerKey = config.twitterConsumerKey, consumerSecret = config.twitterConsumerSecret // Get the Twitter access token from app settings. var accessToken= config.appSettings.TWITTER_ACCESS_TOKEN, accessTokenSecret = config.appSettings.TWITTER_ACCESS_TOKEN_SECRET; Note that this code also retrieves Twitter consumer key values stored in the **Identity** tab in the portal. Because a **config object** is not available in table operation and scheduled job scripts, you must require the configuration module to access app settings. For a complete example, see [Schedule backend jobs in Mobile Services].

Using the command line tool

In Mobile Services, you can create, modify, and delete server scripts by using the Azure command line tool. Before uploading your scripts, make sure that you are using the following directory structure: ![4][4] Note that this directory structure is the same as the git repository when using source control. When uploading script files from the command line tool, you must first navigate to the `.\services\` directory. The following command uploads a script named `todoitem.insert.js` from the `table` subdirectory: ~$azure mobile script upload todolist table/todoitem.insert.js info: Executing command mobile script upload info: mobile script upload command OK The following command returns information about every script file maintained in your mobile service: ~$ azure mobile script list todolist info: Executing command mobile script list + Retrieving script information info: Table scripts data: Name Size data: ------------------------- ---- data: table/channels.insert 1980 data: table/TodoItem.insert 5504 data: table/TodoItem.read 64 info: Shared scripts data: Name Size data: ---------------- ---- data: shared/helper.js 62 data: shared/uuid.js 7452 info: Scheduled job scripts data: Job name Script name Status Interval Last run Next run data: ---------- -------------------- -------- ----------- -------- -------- data: getUpdates scheduler/getUpdates disabled 15 [minute] N/A N/A info: Custom API scripts data: Name Get Put Post Patch Delete data: ---------------------- ----------- ----------- ----------- ----------- ----------- data: completeall application application application application application data: register_notifications application application user application application info: mobile script list command OK For more information, see [Commands to manage Azure Mobile Services]. ## Working with tables This section details strategies for working directly with SQL Database table data, including the following sections: + [Overview of working with tables](#overview-tables) + [How to: Access tables from scripts] + [How to: Perform Bulk Inserts] + [How to: Map JSON types to database types] + [Using Transact-SQL to access tables] ### Overview of working with tables Many scenarios in Mobile Services require server scripts to access tables in the database. For example. because Mobile Services does not preserve state between script executions, any data that needs to be persisted between script executions must be stored in tables. You might also want to examine entries in a permissions table or store audit data instead of just writing to the log, where data has a limited duration and cannot be accessed programmatically. Mobile Services has two ways of accessing tables, either by using a [table object] proxy or by composing Transact-SQL queries using the [mssql object]. The [table object] makes it easy to access table data from your sever script code, but the [mssql object] supports more complex data operations and provides the most flexibility. ### How to: Access tables from scripts The easiest way to access tables from your script is by using the [tables object]. The **getTable** function returns a [table object] instance that's a proxy for accessing the requested table. You can then call functions on the proxy to access and change data. Scripts registered to both table operations and scheduled jobs can access the [tables object] as a global object. This line of code gets a proxy for the *TodoItems* table from the global [tables object]: var todoItemsTable = tables.getTable('TodoItems'); Custom API scripts can access the [tables object] from the service property of the supplied [request object]. This line of code gets [tables object] from the request: var todoItemsTable = request.service.tables.getTable('TodoItem'); > [AZURE.NOTE] Shared functions cannot access the **tables** object directly. In a shared function, you must pass the tables object to the function. Once you have a [table object], you can call one or more table operation functions: insert, update, delete or read. This example reads user permissions from a permissions table: function insert(item, user, request) { var permissionsTable = tables.getTable('permissions'); permissionsTable .where({ userId: user.userId, permission: 'submit order'}) .read({ success: checkPermissions }); function checkPermissions(results) { if(results.length > 0) { // Permission record was found. Continue normal execution. request.execute(); } else { console.log('User %s attempted to submit an order without permissions.', user.userId); request.respond(statusCodes.FORBIDDEN, 'You do not have permission to submit orders.'); } } } The next example writes auditing information to an **audit** table: function update(item, user, request) { request.execute({ success: insertAuditEntry }); function insertAuditEntry() { var auditTable = tables.getTable('audit'); var audit = { record: 'checkins', recordId: item.id, timestamp: new Date(), values: JSON.stringify(item) }; auditTable.insert(audit, { success: function() { // Write to the response now that all data operations are complete request.respond(); } }); } } A final example is in the code sample here: [How to: Access custom parameters][How to: Add custom parameters]. ### How to: Perform Bulk Inserts If you use a **for** or **while** loop to directly insert a large number of items (1000, for example) into a table , you may encounter a SQL connection limit that causes some of the inserts to fail. Your request may never complete or it may return a HTTP 500 Internal Server Error. To avoid this problem, you can insert the items in batches of 10 or so. After the first batch is inserted, submit the next batch, and so on. By using the following script, you can set the size of a batch of records to insert in parallel. We recomend that you keep the number of records small. The function **insertItems** calls itself recursively when an async insert batch has completed. The for loop at the end inserts one record at a time, and calls **insertComplete** on success and **errorHandler** on error. **insertComplete** controls whether **insertItems** will be called recursively for the next batch, or whether the job is done and the script should exit. var todoTable = tables.getTable('TodoItem'); var recordsToInsert = 1000; var batchSize = 10; var totalCount = 0; var errorCount = 0; function insertItems() { var batchCompletedCount = 0; var insertComplete = function() { batchCompletedCount++; totalCount++; if(batchCompletedCount === batchSize || totalCount === recordsToInsert) { if(totalCount < recordsToInsert) { // kick off the next batch insertItems(); } else { // or we are done, report the status of the job // to the log and don't do any more processing console.log("Insert complete. %d Records processed. There were %d errors.", totalCount, errorCount); } } }; var errorHandler = function(err) { errorCount++; console.warn("Ignoring insert failure as part of batch.", err); insertComplete(); }; for(var i = 0; i < batchSize; i++) { var item = { text: "This is item number: " + totalCount + i }; todoTable.insert(item, { success: insertComplete, error: errorHandler }); } } insertItems(); The entire code sample, and accompanying discussion, can be found in this [blog posting](http://blogs.msdn.com/b/jpsanders/archive/2013/03/20/server-script-to-insert-table-items-in-windows-azure-mobile-services.aspx). If you use this code, you can adapt it to your specific situation, and thoroughly test it. ### How to: Map JSON types to database types The collections of data types on the client and in a Mobile Services database table are different. Sometimes they map easily to one another, and other times they don't. Mobile Services performs a number of type transformations in the mapping: - The client language-specific types are serialized into JSON. - The JSON representation is translated into JavaScript before it appears in server scripts. - The JavaScript data types are converted to SQL database types when saved using the [tables object]. The transformation from client schema into JSON varies across platforms. JSON.NET is used in Windows Store and Windows Phone clients. The Android client uses the gson library. The iOS client uses the NSJSONSerialization class. The default serialization behavior of each of these libraries is used, except that date objects are converted to JSON strings that contain the date that's encoded by using ISO 8601. When you are writing server scripts that use [insert], [update], [read] or [delete] functions, you can access the JavaScript representation of your data. Mobile Services uses the Node.js's deserialization function ([JSON.parse](http://es5.github.io/#x15.12)) to transform JSON on the wire into JavaScript objects. However Mobile Services does a transformation to extract **Date** objects from ISO 8601 strings. When you use the [tables object] or the [mssql object], or just let your table scripts execute, the deserialized JavaScript objects are inserted into your SQL database. In that process, object properties are mapped to T-SQL types: JavaScript property|T-SQL type ---|--- Number|Float(53) Boolean|Bit Date|DateTimeOffset(3)| String|Nvarchar(max) Buffer|Not supported Object|Not supported Array|Not supported Stream|Not supported ### Using Transact-SQL to access tables The easiest way to work table data from server scripts is by using a [table object] proxy. However, there are more advanced scenarios that are not supported by the [table object], such as as join queries and other complex queries and invoking stored procedures. In these cases, you must execute Transact-SQL statements directly against the relational table by using the [mssql object]. This object provides the following functions: - **query**: executes a query, specified by a TSQL string; the results are returned to the **success** callback on the **options** object. The query can include parameters if the *params* parameter is present. - **queryRaw**: like *query* except that the result set returned from the query is in a "raw" format (see example below). - **open**: used to get a connection to your Mobile Services database, and you can then use the connection object to invoke database operations such as transactions. These methods give you increasingly more low-level control over the query processing. + [How to: Run a static query] + [How to: Run a dynamic query] + [How to: Join relational tables] + [How to: Run a query that returns *raw* results] + [How to: Get access to a database connection] #### How to: Run a static query The following query has no parameters and returns three records from the `statusupdate` table. The rowset is in standard JSON format. mssql.query('select top 3 * from statusupdates', { success: function(results) { console.log(results); }, error: function(err) { console.log("error is: " + err); } }); #### How to: Run a dynamic parameterized query The following example implements custom authorization by reading permissions for each user from the permissions table. The placeholder (?) is replaced with the supplied parameter when the query is executed. var sql = "SELECT _id FROM permissions WHERE userId = ? AND permission = 'submit order'"; mssql.query(sql, [user.userId], { success: function(results) { if (results.length > 0) { // Permission record was found. Continue normal execution. request.execute(); } else { console.log('User %s attempted to submit an order without permissions.', user.userId); request.respond(statusCodes.FORBIDDEN, 'You do not have permission to submit orders.'); } }, error: function(err) { console.log("error is: " + err); } }); #### How to: Join relational tables You can join two tables by using the **query** method of the [mssql object] to pass in the TSQL code that implements the join. Let's assume we have some items in our **ToDoItem** table and each item in the table has a **priority** property, which corresponds to a column in the table. An item may look like this: { text: 'Take out the trash', complete: false, priority: 1} Let's also assume we have an additional table called **Priority** with rows that contain a priority **number** and a text **description**. For example, the priority number 1 might have the description of "Critical", with the object looking as follows: { number: 1, description: 'Critical'} We can now replace the **priority** number in our item with the text description of the priority number. We do this with a relational join of the two tables. mssql.query('SELECT t.text, t.complete, p.description FROM ToDoItem as t INNER JOIN Priority as p ON t.priority = p.number', { success: function(results) { console.log(results); }, error: function(err) { console.log("error is: " + err); }); The script joins the two tables and writes the results to the log. The resulting objects could look like this: { text: 'Take out the trash', complete: false, description: 'Critical'} #### How to: Run a query that returns *raw* results This example executes the query, as before, but returns the resultset in "raw" format which requires you to parse it, row by row, and column by column. A possible scenario for this is if you need access to data types that Mobile Services does not support. This code simply writes the output to the console log so you can inspect the raw format. mssql.queryRaw('SELECT * FROM ToDoItem', { success: function(results) { console.log(results); }, error: function(err) { console.log("error is: " + err); } }); Here is the output from running this query. It contains metadata about each column in the table, followed by a representation of the rows and columns. { meta: [ { name: 'id', size: 19, nullable: false, type: 'number', sqlType: 'bigint identity' }, { name: 'text', size: 0, nullable: true, type: 'text', sqlType: 'nvarchar' }, { name: 'complete', size: 1, nullable: true, type: 'boolean', sqlType: 'bit' }, { name: 'priority', size: 53, nullable: true, type: 'number', sqlType: 'float' } ], rows: [ [ 1, 'good idea for the future', null, 3 ], [ 2, 'this is important but not so much', null, 2 ], [ 3, 'fix this bug now', null, 0 ], [ 4, 'we need to fix this one real soon now', null, 1 ], ] } #### How to: Get access to a database connection You can use the **open** method to get access to the database connection. One reason to do this might be if you need to use database transactions. Successful execution of the **open** causes the database connection to be passed into the **success** function as a parameter. You can invoke any of the following functions on the **connection** object: *close*, *queryRaw*, *query*, *beginTransaction*, *commit*, and *rollback*. mssql.open({ success: function(connection) { connection.query(//query to execute); }, error: function(err) { console.log("error is: " + err); } }); ## Debugging and troubleshooting The primary way to debug and troubleshoot your server scripts is by writing to the service log. By default, Mobile Services writes errors that occur during service script execution to the service logs. Your scripts can also write to the logs. Writing to logs is great way to debug your scripts and validate that they are behaving as desired. ### How to: Write output to the mobile service logs To write to the logs, use the global [console object]. Use the **log** or **info** function to log information-level warnings. The **warning** and **error** functions log their respective levels, which are called-out in the logs. > [AZURE.NOTE] To view the logs for your mobile service, log on to the [Azure classic portal](https://manage.windowsazure.com/), select your mobile service, and then choose the **Logs** tab. You can also use the logging functions of the [console object] to format your messages using parameters. The following example supplies a JSON object as a parameter to the message string: function insert(item, user, request) { console.log("Inserting item '%j' for user '%j'.", item, user); request.execute(); } Notice that the string `%j` is used as the placeholder for a JSON object and that parameters are supplied in sequential order. To avoid overloading your log, you should remove or disable calls to console.log() that aren't needed for production use. [Introduction]: #intro [Table operations]: #table-scripts [Basic table operations]: #basic-table-ops [How to: Register for table operations]: #register-table-scripts [How to: Define table scripts]: #execute-operation [How to: override the default response]: #override-response [How to: Modify an operation]: #modify-operation [How to: Override success and error]: #override-success-error [How to: Override execute success]: #override-success [How to: Override default error handling]: #override-error [How to: Access tables from scripts]: #access-tables [How to: Add custom parameters]: #access-headers [How to: Work with users]: #work-with-users [How to: Define scheduled job scripts]: #scheduler-scripts [How to: Refine access to tables]: #authorize-tables [Using Transact-SQL to access tables]: #TSQL [How to: Run a static query]: #static-query [How to: Run a dynamic query]: #dynamic-query [How to: Run a query that returns *raw* results]: #raw [How to: Get access to a database connection]: #connection [How to: Join relational tables]: #joins [How to: Perform Bulk Inserts]: #bulk-inserts [How to: Map JSON types to database types]: #JSON-types [How to: Load Node.js modules]: #modules-helper-functions [How to: Write output to the mobile service logs]: #write-to-logs [Source control, shared code, and helper functions]: #shared-code [Using the command line tool]: #command-prompt [Working with tables]: #working-with-tables [Custom API anchor]: #custom-api [How to: Define a custom API]: #define-custom-api [How to: Share code by using source control]: #shared-code-source-control [How to: Use helper functions]: #helper-functions [Debugging and troubleshooting]: #debugging [How to: Implement HTTP methods]: #handle-methods [How to: Work with users and headers in a custom API]: #get-api-user [How to: Access custom API request headers]: #get-api-headers [Job Scheduler]: #scheduler-scripts [How to: Define multiple routes in a custom API]: #api-routes [How to: Send and receive data as XML]: #api-return-xml [How to: Work with app settings]: #app-settings [1]: ./media/mobile-services-how-to-use-server-scripts/1-mobile-insert-script-users.png [2]: ./media/mobile-services-how-to-use-server-scripts/2-mobile-custom-api-script.png [3]: ./media/mobile-services-how-to-use-server-scripts/3-mobile-schedule-job-script.png [4]: ./media/mobile-services-how-to-use-server-scripts/4-mobile-source-local-cli.png [Mobile Services server script reference]: http://msdn.microsoft.com/library/windowsazure/jj554226.aspx [Schedule backend jobs in Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/schedule-backend-tasks/ [request object]: http://msdn.microsoft.com/library/windowsazure/jj554218.aspx [response object]: http://msdn.microsoft.com/library/windowsazure/dn303373.aspx [User object]: http://msdn.microsoft.com/library/windowsazure/jj554220.aspx [push object]: http://msdn.microsoft.com/library/windowsazure/jj554217.aspx [insert function]: http://msdn.microsoft.com/library/windowsazure/jj554229.aspx [insert]: http://msdn.microsoft.com/library/windowsazure/jj554229.aspx [update function]: http://msdn.microsoft.com/library/windowsazure/jj554214.aspx [delete function]: http://msdn.microsoft.com/library/windowsazure/jj554215.aspx [read function]: http://msdn.microsoft.com/library/windowsazure/jj554224.aspx [update]: http://msdn.microsoft.com/library/windowsazure/jj554214.aspx [delete]: http://msdn.microsoft.com/library/windowsazure/jj554215.aspx [read]: http://msdn.microsoft.com/library/windowsazure/jj554224.aspx [query object]: http://msdn.microsoft.com/library/windowsazure/jj613353.aspx [apns object]: http://msdn.microsoft.com/library/windowsazure/jj839711.aspx [mpns object]: http://msdn.microsoft.com/library/windowsazure/jj871025.aspx [wns object]: http://msdn.microsoft.com/library/windowsazure/jj860484.aspx [table object]: http://msdn.microsoft.com/library/windowsazure/jj554210.aspx [tables object]: http://msdn.microsoft.com/library/windowsazure/jj614364.aspx [mssql object]: http://msdn.microsoft.com/library/windowsazure/jj554212.aspx [console object]: http://msdn.microsoft.com/library/windowsazure/jj554209.aspx [Read and write data]: http://msdn.microsoft.com/library/windowsazure/jj631640.aspx [Validate data]: http://msdn.microsoft.com/library/windowsazure/jj631638.aspx [Modify the request]: http://msdn.microsoft.com/library/windowsazure/jj631635.aspx [Modify the response]: http://msdn.microsoft.com/library/windowsazure/jj631631.aspx [Azure classic portal]: https://manage.windowsazure.com/ [Schedule jobs]: http://msdn.microsoft.com/library/windowsazure/jj860528.aspx [Validate and modify data in Mobile Services by using server scripts]: https://azure.microsoft.com/develop/mobile/tutorials/validate-modify-and-augment-data-dotnet/ [Commands to manage Azure Mobile Services]: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Mobile_Scripts [Windows Store Push]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-dotnet/ [Windows Phone Push]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-wp8/ [iOS Push]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-ios/ [Android Push]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-android/ [Azure SDK for Node.js]: http://go.microsoft.com/fwlink/p/?LinkId=275539 [Send HTTP request]: http://msdn.microsoft.com/library/windowsazure/jj631641.aspx [Send email from Mobile Services with SendGrid]: https://azure.microsoft.com/develop/mobile/tutorials/send-email-with-sendgrid/ [Get started with authentication]: http://go.microsoft.com/fwlink/p/?LinkId=287177 [crypto API]: http://go.microsoft.com/fwlink/p/?LinkId=288802 [path API]: http://go.microsoft.com/fwlink/p/?LinkId=288803 [querystring API]: http://go.microsoft.com/fwlink/p/?LinkId=288804 [url API]: http://go.microsoft.com/fwlink/p/?LinkId=288805 [util API]: http://go.microsoft.com/fwlink/p/?LinkId=288806 [zlib API]: http://go.microsoft.com/fwlink/p/?LinkId=288807 [Custom API]: http://msdn.microsoft.com/library/windowsazure/dn280974.aspx [Call a custom API from the client]: https://azure.microsoft.com/develop/mobile/tutorials/call-custom-api-dotnet/#define-custom-api [express.js library]: http://go.microsoft.com/fwlink/p/?LinkId=309046 [Define a custom API that supports periodic notifications]: https://azure.microsoft.com/develop/mobile/tutorials/create-pull-notifications-dotnet/ [express object in express.js]: http://expressjs.com/api.html#express [Store server scripts in source control]: https://azure.microsoft.com/develop/mobile/tutorials/store-scripts-in-source-control/ [Leverage shared code and Node.js modules in your server scripts]: https://azure.microsoft.com/develop/mobile/tutorials/store-scripts-in-source-control/#use-npm [service object]: http://msdn.microsoft.com/library/windowsazure/dn303371.aspx [App settings]: http://msdn.microsoft.com/library/dn529070.aspx [config module]: http://msdn.microsoft.com/library/dn508125.aspx [Support for package.json in Azure Mobile Services]: http://go.microsoft.com/fwlink/p/?LinkId=391036 ================================================ FILE: docs/mobile-services-html-get-started-users.md ================================================ # Add authentication to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md) This topic shows you how to authenticate users in Azure Mobile Services from your HTML app, including PhoneGap or Cordova apps. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario. 3. In the app directory, launch one of the following command files from the **server** subfolder. + **launch-windows** (Windows computers) + **launch-mac.command** (Mac OS X computers) + **launch-linux.sh** (Linux computers) >[AZURE.NOTE]On a Windows computer, type `R` when PowerShell asks you to confirm that you want to run the script. Your web browser might warn you to not run the script because it was downloaded from the internet. When this happens, you must request that the browser proceed to load the script. This starts a web server on your local computer to host the new app. 2. Open the URL http://localhost:8000/ in a web browser to start the app. The data fails to load. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the _TodoItem_ table now requires authentication. 3. (Optional) Open the script debugger for your web browser and reload the page. Verify that an access denied error occurs. Next, you will update the app to allow authentication before requesting resources from the mobile service. ## Add authentication to the app >[AZURE.NOTE]Because the login is performed in a popup, you should invoke the **login** method from a button's click event. Otherwise, many browsers will suppress the login window. 1. Open the project file index.html, locate the H1 element and under it add the following code snippet:
You are logged in as .
You are not logged in.
This enables you to login to Mobile Services from the page. 2. In the page.js file, locate the line of code at the very bottom of the file that calls to the refreshTodoItems function, and replace it with the following code: function refreshAuthDisplay() { var isLoggedIn = client.currentUser !== null; $("#logged-in").toggle(isLoggedIn); $("#logged-out").toggle(!isLoggedIn); if (isLoggedIn) { $("#login-name").text(client.currentUser.userId); refreshTodoItems(); } } function logIn() { client.login("facebook").then(refreshAuthDisplay, function(error){ alert(error); }); } function logOut() { client.logout(); refreshAuthDisplay(); $('#summary').html('You must login to access data.'); } // On page init, fetch the data and set up event handlers $(function () { refreshAuthDisplay(); $('#summary').html('You must login to access data.'); $("#logged-out button").click(logIn); $("#logged-in button").click(logOut); }); This creates a set of functions to handle the authentication process. The user is authenticated by using a Facebook login. If you are using an identity provider other than Facebook, change the value passed to the **login** method above to one of the following: *microsoftaccount*, *facebook*, *twitter*, *google*, or *aad*. >[AZURE.IMPORTANT]In a PhoneGap app, you must also add the following plugins to the project: >
  • phonegap plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git
  • >
  • phonegap plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git
9. Go back to the browser where your app is running, and refresh the page. When you are successfully logged-in, the app should run without errors, and you should be able to query Mobile Services and make updates to data. >[AZURE.NOTE]When you use Internet Explorer, you may receive the error after login: Cannot reach window opener. It may be on a different Internet Explorer zone. This occurs because the pop-up runs in a different security zone (internet) from localhost (intranet). This only affects apps during development using localhost. As a workaround, open the **Security** tab of **Internet Options**, click **Local Intranet**, click **Sites**, and disable **Automatically detect intranet network**. Remember to change this setting back when you are done testing. ## Next steps In the next tutorial, [Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. Learn more about how to use Mobile Services with HTML/JavaScript in [Mobile Services HTML/JavaScript How-to Conceptual Reference] [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [4]: ./media/mobile-services-html-get-started-users/mobile-services-selection.png [5]: ./media/mobile-services-html-get-started-users/mobile-service-uri.png [13]: ./media/mobile-services-html-get-started-users/mobile-identity-tab.png [14]: ./media/mobile-services-html-get-started-users/mobile-portal-data-tables.png [15]: ./media/mobile-services-html-get-started-users/mobile-portal-change-table-perms.png [Get started with Mobile Services]: mobile-services-html-get-started.md [Authorize users with scripts]: mobile-services-javascript-backend-service-side-authorization.md [Mobile Services HTML/JavaScript How-to Conceptual Reference]: mobile-services-html-how-to-use-client-library.md ================================================ FILE: docs/mobile-services-html-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.TIP] This topic shows you how to get started with Mobile Services as quickly as possible. It is designed for customers new to this Azure feature. If you are already familiar with Mobile Services or are looking for more in-depth information, please select a topic from the left-navigation or see the relevant links in [Next steps](#next-steps). ## Overview This tutorial shows you how to add a cloud-based backend service to an HTML app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple *To do list* app that stores app data in the new mobile service. You can view the following video version of this tutorial. > [AZURE.VIDEO mobile-get-started-html] A screenshot from the completed app is below: ![][0] Completing this tutorial is a prerequisite for all other Mobile Services tutorials for HTML apps. For a PhoneGap/Cordova app, see the the [PhoneGap/Cordova version](mobile-services-javascript-backend-phonegap-get-started.md) of this tutorial. ## Prerequisites The following are required to complete this tutorial: + You must have one of the following web servers running on your local computer: + **On Windows**: IIS Express. IIS Express is installed by the [Microsoft Web Platform Installer]. + **On MacOS X**: Python, which should already be installed. + **On Linux**: Python. You must install the [latest version of Python]. You can use any web server to host the app, but these are the web servers that are supported by the downloaded scripts. + A web browser that supports HTML5. + An Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A0E0E5C02&returnurl=http%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fdevelop%2Fmobile%2Ftutorials%2Fget-started-html%2F"%20target="_blank). ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new HTML app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will create a new HTML app that is connected to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Windows** under **Choose platform** and expand **Create a new HTML app**. ![][6] This displays the three easy steps to create and host an HTML app connected to your mobile service. ![][7] 3. Click **Create TodoItems table** to create a table to store app data. 4. Under **Download and run your app**, click **Download**. This downloads the website files for the sample _To do list_ application that is connected to your mobile service. Save the compressed file to your local computer, and make a note of where you save it. 5. In the **Configure** tab, verify that `localhost` is already listed in the **Allow requests from host names** list under **Cross-Origin Resource Sharing (CORS)**. If it's not, type `localhost` in the **Host name** field and then click **Save**. ![][9] > [AZURE.IMPORTANT] If you deploy the quickstart app to a web server other than localhost, you must add the host name of the web server to the **Allow requests from host names** list. For more information, see [Cross-origin resource sharing](http://msdn.microsoft.com/library/windowsazure/dn155871.aspx). ## Host and run your HTML app The final stage of this tutorial is to host and run your new app on your local computer. 1. Browse to the location where you saved the compressed project files, expand the files on your computer, and launch one of the following command files from the **server** subfolder. + **launch-windows** (Windows computers) + **launch-mac.command** (Mac OS X computers) + **launch-linux.sh** (Linux computers) > [AZURE.NOTE] On a Windows computer, type `R` when PowerShell asks you to confirm that you want to run the script. Your web browser might warn you to not run the script because it was downloaded from the internet. When this happens, you must request that the browser proceed to load the script. This starts a web server on your local computer to host the new app. 2. Open the URL http://localhost:8000/ in a web browser to start the app. 3. In the app, type meaningful text, such as _Complete the tutorial_, in **Enter new task**, and then click **Add**. ![][10] This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the second column in the app. > [AZURE.NOTE] You can review the code that accesses your mobile service to query and insert data, which is found in the page.js file. 4. Back in the [Azure classic portal], click the **Data** tab and then click the **TodoItems** table. ![][11] This lets you browse the data inserted by the app into the table. ![][12] ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * **[Add authentication to your app]** Learn how to authenticate users of your app with an identity provider. * **[Mobile Services HTML/JavaScript How-to Conceptual Reference]** Learn more about how to use Mobile Services with HTML/JavaScript [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [0]: ./media/mobile-services-html-get-started/mobile-quickstart-completed-html.png [6]: ./media/mobile-services-html-get-started/mobile-portal-quickstart-html.png [7]: ./media/mobile-services-html-get-started/mobile-quickstart-steps-html.png [9]: ./media/mobile-services-html-get-started/mobile-services-set-cors-localhost.png [10]: ./media/mobile-services-html-get-started/mobile-quickstart-startup-html.png [11]: ./media/mobile-services-html-get-started/mobile-data-tab.png [12]: ./media/mobile-services-html-get-started/mobile-data-browse.png [Add authentication to your app]: mobile-services-html-get-started-users.md [Azure classic portal]: https://manage.windowsazure.com/ [Microsoft Web Platform Installer]: http://go.microsoft.com/fwlink/p/?LinkId=286333 [latest version of Python]: http://go.microsoft.com/fwlink/p/?LinkId=286342 [Mobile Services HTML/JavaScript How-to Conceptual Reference]: mobile-services-html-how-to-use-client-library.md [Cross-origin resource sharing]: http://msdn.microsoft.com/library/azure/dn155871.aspx ================================================ FILE: docs/mobile-services-html-how-to-use-client-library.md ================================================ # How to use an HTML/JavaScript client for Azure Mobile Services > [AZURE.SELECTOR] - [Android](mobile-services-android-how-to-use-client-library.md) - [HTML/JavaScript](mobile-services-html-how-to-use-client-library.md) - [iOS](mobile-services-ios-how-to-use-client-library.md) - [Managed (Windows/Xamarin)](mobile-services-dotnet-how-to-use-client-library.md) ## Overview This guide shows you how to perform common scenarios using an HTML/JavaScript client for Azure Mobile Services, which includes Windows Store JavaScript and PhoneGap/Cordova apps. The scenarios covered include querying for data, inserting, updating, and deleting data, authenticating users, and handling errors. If you are new to Mobile Services, you should consider first completing the [Mobile Services quickstart](mobile-services-html-get-started.md). The quickstart tutorial helps you configure your account and create your first mobile service. ## What is Mobile Services Azure Mobile Services is a highly scalable mobile application development platform that lets you add enhanced functionality to your mobile device apps by using Azure. With Mobile Services you can: + **Build native and cross platform apps** - Connect your iOS, Android, Windows, or cross-platform Xamarin or Cordova (Phonegap) apps to your backend mobile service using native SDKs. + **Send push notifications to your users** - Send push notifications to your users of your app. + **Authenticate your users** - Leverage popular identity providers like Facebook and Twitter to authenticate your app users. + **Store data in the cloud** - Store user data in a SQL Database (by default) or in Mongo DB, DocumentDB, Azure Tables, or Azure Blobs. + **Build offline-ready apps with sync** - Make your apps work offline and use Mobile Services to sync data in the background. + **Monitor and scale your apps** - Monitor app usage and scale your backend as demand grows. ## Mobile Services Concepts The following are important features and concepts in the Mobile Services: + **Application key:** a unique value that is used to limit access to your mobile service from random clients; this "key" is not a security token and is not used to authenticate users of your app. + **Backend:** the mobile service instance that supports your app. A mobile service is implemented either as an ASP.NET Web API project (*.NET backend* ) or as a Node.js project (*JavaScript backend*). + **Identity provider:** an external service, trusted by Mobile Services, that authenticates your app's users. Supported providers include: Facebook, Twitter, Google, Microsoft Account, and Azure Active Directory. + **Push notification:** Service-initiated message that is sent to a registered device or user using Azure Notification Hubs. + **Scale:** The ability to add, for an additional cost, more processing power, performance, and storage as your app becomes more popular. + **Scheduled job:** Custom code that is run either on a pre-determined schedule or on-demand. For more information, see [Mobile Services Concepts](./ mobile-services-concepts-links.md). ## How to: Create the Mobile Services client The way that you add a reference to the Mobile Services client depends on your app platform, which includes the following: - For a web-based application, open the HTML file and add the following to the script references for the page: - For a Windows Store app written in JavaScript/HTML, add the **WindowsAzure.MobileServices.WinJS** NuGet package to your project. - For a PhoneGap or Cordova app, add the [Mobile Services plugin](https://github.com/Azure/azure-mobile-services-cordova) to your project. This plugin supports [push notifications](#push-notifications). In the editor, open or create a JavaScript file, and add the following code that defines the `MobileServiceClient` variable, and supply the application URL and application key from the mobile service in the `MobileServiceClient` constructor, in that order. var MobileServiceClient = WindowsAzure.MobileServiceClient; var client = new MobileServiceClient('AppUrl', 'AppKey'); You must replace the placeholder `AppUrl` with the application URL of your mobile service and `AppKey` with the application key, which you obtain from the [Azure classic portal](http://manage.windowsazure.com/). >[AZURE.IMPORTANT]The application key is intended to filter-out random request against your mobile service, and it is distributed with the application. Because this key is not encrypted, it cannot be considered secure. To truly secure your mobile service data, you must instead authenticate users before allowing access. For more information, see [How to: Authenticate users](#authentication). ## How to: Query data from a mobile service All of the code that accesses or modifies data in the SQL Database table calls functions on the `MobileServiceTable` object. You get a reference to the table by calling the `getTable()` function on an instance of the `MobileServiceClient`. var todoItemTable = client.getTable('todoitem'); ### How to: Filter returned data The following code illustrates how to filter data by including a `where` clause in a query. It returns all items from `todoItemTable` whose complete field is equal to `false`. `todoItemTable` is the reference to the mobile service table that we created previously. The where function applies a row filtering predicate to the query against the table. It accepts as its argument a JSON object or function that defines the row filter, and returns a query that can be further composed. var query = todoItemTable.where({ complete: false }).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); By calling `where` on the Query object and passing an object as a parameter, we are instructing Mobile Services to return only the rows whose `complete` column contains the `false` value. Also, look at the request URI below, and notice that we are modifying the query string itself: GET /tables/todoitem?$filter=(complete+eq+false) HTTP/1.1 You can view the URI of the request sent to the mobile service by using message inspection software, such as browser developer tools or Fiddler. This request would normally be translated roughly into the following SQL query on the server side: SELECT * FROM TodoItem WHERE ISNULL(complete, 0) = 0 The object which is passed to the `where` method can have an arbitrary number of parameters, and they'll all be interpreted as AND clauses to the query. For example, the line below: query.where({ complete: false, assignee: "david", difficulty: "medium" }).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); Would be roughly translated (for the same request shown before) as SELECT * FROM TodoItem WHERE ISNULL(complete, 0) = 0 AND assignee = 'david' AND difficulty = 'medium' The `where` statement above and the SQL query above find incomplete items assigned to "david" of "medium" difficulty. There is, however, another way to write the same query. A `.where` call on the Query object will add an `AND` expression to the `WHERE` clause, so we could have written that in three lines instead: query.where({ complete: false }); query.where({ assignee: "david" }); query.where({ difficulty: "medium" }); Or using the fluent API: query.where({ complete: false }) .where({ assignee: "david" }) .where({ difficulty: "medium" }); The two methods are equivalent and may be used interchangeably. All the `where` calls so far take an object with some parameters, and are compared for equality against the data from the database. There is, however, another overload for the query method, which takes a function instead of the object. In this function we can then write more complex expressions, using operators such as inequality and other relational operations. In these functions, the keyword `this` binds to the server object. The body of the function is translated into an Open Data Protocol (OData) boolean expression which is passed to a query string parameter. It is possible to pass in a function that takes no parameters, like so: query.where(function () { return this.assignee == "david" && (this.difficulty == "medium" || this.difficulty == "low"); }).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); If passing in a function with parameters, any arguments after the `where` clause are bound to the function parameters in order. Any objects which come from the outside of the function scope MUST be passed as parameters - the function cannot capture any external variables. In the next two examples, the argument "david" is bound to the parameter `name` and in the first example, the argument "medium" is also bound to the parameter `level`. Also, the function must consist of a single `return` statement with a supported expression, like so: query.where(function (name, level) { return this.assignee == name && this.difficulty == level; }, "david", "medium").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); So, as long as we follow the rules, we can add more complex filters to our database queries, like so: query.where(function (name) { return this.assignee == name && (this.difficulty == "medium" || this.difficulty == "low"); }, "david").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); It is possible to combine `where` with `orderBy`, `take`, and `skip`. See the next section for details. ### How to: Sort returned data The following code illustrates how to sort data by including an `orderBy` or `orderByDescending` function in the query. It returns items from `todoItemTable` sorted ascending by the `text` field. By default, the server returns only the first 50 elements. > [AZURE.NOTE] A server-driven page size us used by default to prevent all elements from being returned. This keeps default requests for large data sets from negatively impacting the service. You may increase the number of items to be returned by calling `take` as described in the next section. `todoItemTable` is the reference to the mobile service table that we created previously. var ascendingSortedTable = todoItemTable.orderBy("text").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); var descendingSortedTable = todoItemTable.orderByDescending("text").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); var descendingSortedTable = todoItemTable.orderBy("text").orderByDescending("text").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); ### How to: Return data in pages By default, Mobile Services only returns 50 rows in a given request, unless the client explicitly asks for more data in the response. The following code shows how to implement paging in returned data by using the `take` and `skip` clauses in the query. The following query, when executed, returns the top three items in the table. var query = todoItemTable.take(3).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); Notice that the `take(3)` method was translated into the query option `$top=3` in the query URI. The following revised query skips the first three results and returns the next three after that. This is effectively the second "page" of data, where the page size is three items. var query = todoItemTable.skip(3).take(3).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); Again, you can view the URI of the request sent to the mobile service. Notice that the `skip(3)` method was translated into the query option `$skip=3` in the query URI. This is a simplified scenario of passing hard-coded paging values to the `take` and `skip` functions. In a real-world app, you can use queries similar to the above with a pager control or comparable UI to let users navigate to previous and next pages. ### How to: Select specific columns You can specify which set of properties to include in the results by adding a `select` clause to your query. For example, the following code returns the `id`, `complete`, and `text` properties from each row in the `todoItemTable`: var query = todoItemTable.select("id", "complete", "text").read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }) Here the parameters to the select function are the names of the table's columns that you want to return. All the functions described so far are additive, so we can just keep calling them and we'll each time affect more of the query. One more example: query.where({ complete: false }) .select('id', 'assignee') .orderBy('assignee') .take(10) .read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); ### How to: Look up data by ID The `lookup` function takes only the `id` value, and returns the object from the database with that ID. Database tables are created with either an integer or string `id` column. A string `id` column is the default. todoItemTable.lookup("37BBF396-11F0-4B39-85C8-B319C729AF6D").done(function (result) { alert(JSON.stringify(result)); }, function (err) { alert("Error: " + err); }) ## Execute an OData query operation Mobile Services uses the OData query URI conventions for composing and executing REST queries. Not all OData queries can be composed by using the built-in query functions, especially complex filter operations like searching for a substring in a property. For these kinds of complex queries, you can pass any valid OData query option string to the `read` function, as follows: function refreshTodoItems() { todoItemTable.read("$filter=substringof('search_text',text)").then(function(items) { var itemElements = $.map(items, createUiForTodoItem); $("#todo-items").empty().append(itemElements); $("#no-items").toggle(items.length === 0); }, handleError); } >[AZURE.NOTE]When you provide a raw OData query option string into the `read` function, you cannot also use the query builder methods in the same query. In this case, you must compose your whole query as an OData query string. For more information on OData system query options, see the [OData system query options reference]. ## How to: Insert data into a mobile service The following code illustrates how to insert new rows into a table. The client requests that a row of data be inserted by sending a POST request to the mobile service. The request body contains the data to be inserted, as a JSON object. todoItemTable.insert({ text: "New Item", complete: false }) This inserts data from the supplied JSON object into the table. You can also specify a callback function to be invoked when the insertion is complete: todoItemTable.insert({ text: "New Item", complete: false }).done(function (result) { alert(JSON.stringify(result)); }, function (err) { alert("Error: " + err); }); ### Working with ID values Mobile Services supports unique custom string values for the table's **id** column. This allows applications to use custom values such as email addresses or user names for the ID. For example, the following code inserts a new item as a JSON object, where the unique ID is an email address: todoItemTable.insert({ id: "myemail@domain.com", text: "New Item", complete: false }); String IDs provide you with the following benefits: + IDs are generated without making a round-trip to the database. + Records are easier to merge from different tables or databases. + IDs values can integrate better with an application's logic. When a string ID value is not already set on an inserted record, Mobile Services generates a unique value for the ID. For more information on how to generate your own ID values, either on the client or in a .NET backend, see [How to: Generate unique ID values](mobile-services-how-to-use-server-scripts.md#generate-guids). You can also use integer IDs for your tables. To use an integer ID, you must create your table with the `mobile table create` command using the `--integerId` option. This command is used with the Command-line Interface (CLI) for Azure. For more information on using the CLI, see [CLI to manage Mobile Services tables](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Mobile_Tables). ## How to: Modify data in a mobile service The following code illustrates how to update data in a table. The client requests that a row of data be updated by sending a PATCH request to the mobile service. The request body contains the specific fields to be updated, as a JSON object. It updates an existing item in the table `todoItemTable`. todoItemTable.update({ id: idToUpdate, text: newText }) The first parameter specifies the instance to update in the table, as specified by its ID. You can also specify a callback function to be invoked when the update is complete: todoItemTable.update({ id: idToUpdate, text: newText }).done(function (result) { alert(JSON.stringify(result)); }, function (err) { alert("Error: " + err); }); ## How to: Delete data in a mobile service The following code illustrates how to delete data from a table. The client requests that a row of data be deleted by sending a DELETE request to the mobile service. It deletes an existing item in the table todoItemTable. todoItemTable.del({ id: idToDelete }) The first parameter specifies the instance to delete in the table, as specified by its ID. You can also specify a callback function to be invoked when the delete is complete: todoItemTable.del({ id: idToDelete }).done(function () { /* Do something */ }, function (err) { alert("Error: " + err); }); ## How to: Display data in the user interface This section shows how to display returned data objects using UI elements. To query items in `todoItemTable` and display it in a very simple list, you can run the following example code. No selection, filtering or sorting of any kind is done. var query = todoItemTable; query.read().then(function (todoItems) { // The space specified by 'placeToInsert' is an unordered list element
    ...
var listOfItems = document.getElementById('placeToInsert'); for (var i = 0; i < todoItems.length; i++) { var li = document.createElement('li'); var div = document.createElement('div'); div.innerText = todoItems[i].text; li.appendChild(div); listOfItems.appendChild(li); } }).read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); In a Windows Store app, the results of a query can be used to create a [WinJS.Binding.List] object, which can be bound as the data source for a [ListView] object. For more information, see [Data binding (Windows Store apps using JavaScript and HTML)]. ## How to: Call a custom API A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON. For an example of how to create a custom API in your mobile service, see [How to: define a custom API endpoint](mobile-services-dotnet-backend-define-custom-api.md). You call a custom API from the client by calling the [invokeApi](https://github.com/Azure/azure-mobile-services/blob/master/sdk/Javascript/src/MobileServiceClient.js#L337) method on **MobileServiceClient**. For example, the following line of code sends a POST request to the **completeAll** API on the mobile service: client.invokeApi("completeall", { body: null, method: "post" }).done(function (results) { var message = results.result.count + " item(s) marked as complete."; alert(message); refreshTodoItems(); }, function(error) { alert(error.message); }); For more realistic examples and a more a complete discussion of **invokeApi**, see [Custom API in Azure Mobile Services Client SDKs](http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/19/custom-api-in-azure-mobile-services-client-sdks.aspx). ## How to: Authenticate users Mobile Services supports authenticating and authorizing app users using a variety of external identity providers: Facebook, Google, Microsoft Account, and Twitter. You can set permissions on tables to restrict access for specific operations to only authenticated users. You can also use the identity of authenticated users to implement authorization rules in server scripts. For more information, see the [Get started with authentication] tutorial. >[AZURE.NOTE] When using authentication in a PhoneGap or Cordova app, you must also add the following plugins to the project: > >+ https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git >+ https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git Two authentication flows are supported: a _server flow_ and a _client flow_. The server flow provides the simplest authentication experience, as it relies on the provider's web authentication interface. The client flow allows for deeper integration with device-specific capabilities such as single-sign-on as it relies on provider-specific device-specific SDKs. ### Server flow To have Mobile Services manage the authentication process in your Windows Store or HTML5 app, you must register your app with your identity provider. Then in your mobile service, you need to configure the application ID and secret provided by your provider. For more information, see the tutorial [Add authentication to your app](mobile-services-html-get-started-users.md). Once you have registered your identity provider, simply call the [LoginAsync method] with the [MobileServiceAuthenticationProvider] value of your provider. For example, to login with Facebook use the following code. client.login("facebook").done(function (results) { alert("You are now logged in as: " + results.userId); }, function (err) { alert("Error: " + err); }); If you are using an identity provider other than Facebook, change the value passed to the `login` method above to one of the following: `microsoftaccount`, `facebook`, `twitter`, `google`, or `windowsazureactivedirectory`. In this case, Mobile Services manages the OAuth 2.0 authentication flow by displaying the login page of the selected provider and generating a Mobile Services authentication token after successful login with the identity provider. The [login] function, when complete, returns a JSON object (**user**) that exposes both the user ID and Mobile Services authentication token in the **userId** and **authenticationToken** fields, respectively. This token can be cached and re-used until it expires. For more information, see [Caching the authentication token]. ### Client flow Your app can also independently contact the identity provider and then provide the returned token to Mobile Services for authentication. This client flow enables you to provide a single sign-in experience for users or to retrieve additional user data from the identity provider. #### Facebook/Google SDK basic example This example uses Facebook client SDK for authentication: client.login( "facebook", {"access_token": token}) .done(function (results) { alert("You are now logged in as: " + results.userId); }, function (err) { alert("Error: " + err); }); This example assumes that the token provided by the respective provider SDK is stored in the `token` variable. Twitter cannot be used for client authentication at this time. #### Microsoft Account basic example The following example uses the Live SDK, which supports single-sign-on for Windows Store apps by using Microsoft Account: WL.login({ scope: "wl.basic"}).then(function (result) { client.login( "microsoftaccount", {"authenticationToken": result.session.authentication_token}) .done(function(results){ alert("You are now logged in as: " + results.userId); }, function(error){ alert("Error: " + err); }); }); This simplified example gets a token from Live Connect, which is supplied to Mobile Services by calling the [login] function. #### Microsoft Account complete example The following example shows how to use the Live SDK with WinJS APIs to provide an enhanced single-sign-on experience: // Set the mobileClient variable to client variable generated by the tooling. var mobileClient = ; var session = null; var login = function () { return new WinJS.Promise(function (complete) { WL.login({ scope: "wl.basic" }).then(function (result) { session = result.session; WinJS.Promise.join([ WL.api({ path: "me", method: "GET" }), mobileClient.login(result.session.authentication_token) ]).done(function (results) { // Build the welcome message from the Microsoft account info. var profile = results[0]; var title = "Welcome " + profile.first_name + "!"; var message = "You are now logged in as: " + mobileClient.currentUser.userId; var dialog = new Windows.UI.Popups.MessageDialog(message, title); dialog.showAsync().then(function () { // Reload items from the mobile service. refreshTodoItems(); }).done(complete); }, function (error) { }); }, function (error) { session = null; var dialog = new Windows.UI.Popups.MessageDialog("You must log in.", "Login Required"); dialog.showAsync().done(complete); }); }); } var authenticate = function () { // Block until sign-in is successful. login().then(function () { if (session === null) { // Authentication failed, try again. authenticate(); } }); } // Initialize the Live client. WL.init({ redirect_uri: mobileClient.applicationUrl }); // Start the sign-in process. authenticate(); This initializes the Live Connect client, sends a new login request to Microsoft account, sends the returned authentication token to Mobile Services, and then displays information about the signed-in user. The app does not start until authentication succeeds. ## How to: Register for push notifications When your app is a PhoneGap or Apache Cordova HTML/JavaScript app, the native mobile platform enables you to receive push notifications on the device. The [Apache Cordova plugin for Azure Mobile Services](https://github.com/Azure/azure-mobile-services-cordova) enables you to register for push notifications with Azure Notification Hubs. The specific notification service used depends on the native device platform on which the code executes. For an example of how to do this, see the sample, [Use Microsoft Azure to push notifications to Cordova apps](https://github.com/Azure/mobile-services-samples/tree/master/CordovaNotificationsArticle). >[AZURE.NOTE]This plugin currently only supports iOS and Android devices. For a solution that also includes Windows devices, see the article [Push Notifications to PhoneGap Apps using Notification Hubs Integration](http://blogs.msdn.com/b/azuremobile/archive/2014/06/17/push-notifications-to-phonegap-apps-using-notification-hubs-integration.aspx). ## How to: Handle errors There are several ways to encounter, validate, and work around errors in Mobile Services. As an example, server scripts are registered in a mobile service and can be used to perform a wide range of operations on data being inserted and updated, including validation and data modification. Imagine defining and registering a server script that validate and modify data, like so: function insert(item, user, request) { if (item.text.length > 10) { request.respond(statusCodes.BAD_REQUEST, { error: "Text cannot exceed 10 characters" }); } else { request.execute(); } } This server-side script validates the length of string data sent to the mobile service and rejects strings that are too long, in this case longer than 10 characters. Now that the mobile service is validating data and sending error responses on the server-side, you can update your HTML app to be able to handle error responses from validation. todoItemTable.insert({ text: itemText, complete: false }) .then(function (results) { alert(JSON.stringify(results)); }, function (error) { alert(JSON.parse(error.request.responseText).error); }); To tease this out even further, you pass in the error handler as the second argument each time you perform data access: function handleError(message) { if (window.console && window.console.error) { window.console.error(message); } } client.getTable("tablename").read() .then(function (data) { /* do something */ }, handleError); ## How to: Use promises Promises provide a mechanism to schedule work to be done on a value that has not yet been computed. It is an abstraction for managing interactions with asynchronous APIs. The `done` promise is executed as soon as the function provided to it has either successfully completed or has gotten an error. Unlike the `then` promise, it is guaranteed to throw any error that is not handled inside the function, and after the handlers have finished executing, this function throws any error that would have been returned from then as a promise in the error state. For more information, see [done]. promise.done(onComplete, onError); Like so: var query = todoItemTable; query.read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); The `then` promise is the same as the as the `done` promise, but unlike the `then` promise, `done` is guaranteed to throw any error that is not handled inside the function. If you do not provide an error handler to `then` and the operation has an error, it does not throw an exception but rather returns a promise in the error state. For more information, see [then]. promise.then(onComplete, onError).done( /* Your success and error handlers */ ); Like so: var query = todoItemTable; query.read().done(function (results) { alert(JSON.stringify(results)); }, function (err) { alert("Error: " + err); }); You can use promises in a number of different ways. You can chain promise operations by calling `then` or `done` on the promise that is returned by the previous `then` function. Use `then` for an intermediate stage of the operation (for example `.then().then()`), and `done` for the final stage of the operation (for example `.then().then().done()`). You can chain multiple `then` functions, because `then` returns a promise. You cannot chain more than one `done` method, because it returns undefined. [Learn more about the differences between then and done]. todoItemTable.insert({ text: "foo" }).then(function (inserted) { inserted.newField = 123; return todoItemTable.update(inserted); }).done(function (insertedAndUpdated) { alert(JSON.stringify(insertedAndUpdated)); }) ## How to: Customize client request headers You can send custom request headers using the `withFilter` function, reading and writing arbitrary properties of the request about to be sent within the filter. You may want to add such a custom HTTP header if a server-side script needs it or may be enhanced by it. var client = new WindowsAzure.MobileServiceClient('https://your-app-url', 'your-key') .withFilter(function (request, next, callback) { request.headers.MyCustomHttpHeader = "Some value"; next(request, callback); }); Filters are used for a lot more than customizing request headers. They can be used to examine or change requests, examine or change responses, bypass networking calls, send multiple calls, etc. ## How to: Use cross-origin resource sharing To control which websites are allowed to interact with and send requests to your mobile service, make sure to add the host name of the website you use to host it to the Cross-Origin Resource Sharing (CORS) whitelist. For a JavaScript backend mobile service, you can configure the whitelist on the Configure tab in the [Azure classic portal](https://manage.windowsazure.com). You can use wildcards if required. By default, new Mobile Services instruct browsers to permit access only from `localhost`, and Cross-Origin Resource Sharing (CORS) allows JavaScript code running in a browser on an external hostname to interact with your Mobile Service. This configuration is not necessary for WinJS applications. [What is Mobile Services]: #what-is [Concepts]: #concepts [How to: Create the Mobile Services client]: #create-client [How to: Query data from a mobile service]: #querying [Filter returned data]: #filtering [Sort returned data]: #sorting [Return data in pages]: #paging [Select specific columns]: #selecting [Look up data by ID]: #lookingup [How to: Display data in the user interface]: #binding [How to: Insert data into a mobile service]: #inserting [How to: Modify data in a mobile service]: #modifying [How to: Delete data in a mobile service]: #deleting [How to: Authenticate users]: #authentication [How to: Handle errors]: #errors [How to: Use promises]: #promises [How to: Customize request headers]: #customizing [How to: Use cross-origin resource sharing]: #hostnames [Next steps]: #nextsteps [Execute an OData query operation]: #odata-query [then]: http://msdn.microsoft.com/library/windows/apps/br229728.aspx [done]: http://msdn.microsoft.com/library/windows/apps/hh701079.aspx [Learn more about the differences between then and done]: http://msdn.microsoft.com/library/windows/apps/hh700334.aspx [how to handle errors in promises]: http://msdn.microsoft.com/library/windows/apps/hh700337.aspx [sessionStorage]: http://msdn.microsoft.com/library/cc197062(v=vs.85).aspx [localStorage]: http://msdn.microsoft.com/library/cc197062(v=vs.85).aspx [ListView]: http://msdn.microsoft.com/library/windows/apps/br211837.aspx [Data binding (Windows Store apps using JavaScript and HTML)]: http://msdn.microsoft.com/library/windows/apps/hh758311.aspx [login]: https://github.com/Azure/azure-mobile-services/blob/master/sdk/Javascript/src/MobileServiceClient.js#L301 [ASCII control codes C0 and C1]: http://en.wikipedia.org/wiki/Data_link_escape_character#C1_set [OData system query options reference]: http://go.microsoft.com/fwlink/p/?LinkId=444502 ================================================ FILE: docs/mobile-services-ios-get-started-offline-data.md ================================================ # Get Started with Offline Data Sync in Mobile Services > [AZURE.SELECTOR] - [Android)](mobile-services-android-get-started-offline-data.md) - [iOS](mobile-services-ios-get-started-offline-data.md) - [Windows](mobile-services-windows-store-dotnet-get-started-offline-data.md) - [Xamarin.Android](mobile-services-xamarin-android-get-started-offline-data.md) - [Xamarin.iOS](mobile-services-xamarin-ios-get-started-offline-data.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). Offline sync allows you to view, add, or modify data in a mobile app even when there is no network connection. In this tutorial, you'll learn how your app can automatically store changes in a local offline database and sync those changes whenever it's back online. Offline sync has several advantages: * Improves app responsiveness by caching server data locally on device * Makes apps resilient against intermittent network connectivity * Allows you to create and modify data even with little or no connectivity * Syncs data across multiple devices * Detects conflicts when same record is modified by two devices > [AZURE.NOTE] To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get [free mobile services that you can keep using even after your trial ends](https://azure.microsoft.com/pricing/details/mobile-services/). For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=AE564AB28 target="_blank"). This tutorial is based on the [Mobile Services Quick Start tutorial], which you must complete first. Let's first review the code related to offline sync already in the Quick Start. ## Review Mobile Services Sync Code Azure Mobile Services offline sync allows end users to interact with a local database when the network is not accessible. To use these features in your app, you initialize the sync context of `MSClient` and reference a local store. Then reference your table through the `MSSyncTable` interface. * In **QSTodoService.m**, notice the type of member `syncTable` is `MSSyncTable`. Offline sync uses this instead of `MSTable`. When a sync table is used, all operations go to the local store and are synchronized with the remote service only with explicit push and pull operations. ``` @property (nonatomic, strong) MSSyncTable *syncTable; ``` To get a reference to a sync table, use the method `syncTableWithName`. To remove offline sync functionality, use `tableWithName` instead. * In **QSTodoService.m**, before table operations are performed, the local store is initialized in `QSTodoService.init`: ``` MSCoreDataStore *store = [[MSCoreDataStore alloc] initWithManagedObjectContext:context]; self.client.syncContext = [[MSSyncContext alloc] initWithDelegate:nil dataSource:store callback:nil]; ``` This creates a local store using the `MSCoreDataStore` interface. You may provide a different local store by implementing the `MSSyncContextDataSource` protocol. The first parameter of `initWithDelegate` specifies a conflict handler, but since we've passed `nil`, we get the default conflict handler which fails on any conflict. For details on how to implement a custom conflict handler, see [Handling Conflicts with Offline Support for Mobile Services]. * In **QSTodoService.m**, `syncData` first pushes new changes, and then calls `pullData` to get data from the remote service. In `syncData`, we first call `pushWithCompletion` on the sync context. This method is a member of `MSSyncContext` -- rather than the sync table itself -- because it pushes changes across all tables. Only records that are modified in some way locally -- through creation, update, or delete operations -- are sent to the server. At the end of `syncData`, we call the helper `pullData`. In this example, the push operation is not strictly necessary. If there are changes pending in the sync context for the table that is doing a push operation, pull always issues a push first. However, if you have more than one sync table, call push explicitly to have consistency across tables. ``` -(void)syncData:(QSCompletionBlock)completion { // push all changes in the sync context, then pull new data [self.client.syncContext pushWithCompletion:^(NSError *error) { [self logErrorIfNotNil:error]; [self pullData:completion]; }]; } ``` * Next in **QSTodoService.m**, `pullData` gets new data that matches a query. `pullData` calls `MSSyncTable.pullWithQuery` to retrieve remote data and store it locally. `pullWithQuery` also allows you to specify a query to filter the records you wish to retrieve. In this example, the query just retrieves all records in the remote `TodoItem` table. The second parameter to `pullWithQuery` is a query ID for _incremental sync_. Incremental sync retrieves only those records modified since the last sync, using the record's `UpdatedAt` timestamp, called `ms_updatedAt` in the local store. The query ID is descriptive string that is unique for each logical query in your app. To opt-out of incremental sync, pass `nil` as the query ID. This is inefficient since it will retrieve all records on every pull operation. ``` -(void)pullData:(QSCompletionBlock)completion { MSQuery *query = [self.syncTable query]; // Pulls data from the remote server into the local table. // We're pulling all items and filtering in the view // query ID is used for incremental sync [self.syncTable pullWithQuery:query queryId:@"allTodoItems" completion:^(NSError *error) { [self logErrorIfNotNil:error]; // Let the caller know that we have finished if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); } }]; } ``` >[AZURE.NOTE] To remove records from the device local store when they have been deleted in your mobile service database, enable [Soft Delete]. Otherwise, your app should periodically call `MSSyncTable.purgeWithQuery` to purge the local store. * In **QSTodoService.m**, the methods `addItem` and `completeItem` invoke `syncData` after modifying data. In **QSTodoListViewController.m**, the method `refresh` also invokes `syncData` so that the UI displays the latest data on every refresh and at launch (`init` calls `refresh`.) Because the app calls `syncData` whenever you modify data, the app assumes you are online whenever you edit data in the app. ## Review Core Data Model When using the Core Data offline store, you need to define particular tables and fields in your data model. The sample app already includes a data model with the right format. In this section we walk through these tables and how they are used. - Open **QSDataModel.xcdatamodeld**. There are four tables defined, three used by the SDK and one for the todo items themselves: * MS_TableOperations: For tracking items to be synchronized with server * MS_TableOperationErrors: For tracking errors that happen during offline sync * MS_TableConfig: For tracking last updated time for last sync operation for all pull operations * TodoItem: For storing todo items. The system columns **ms_createdAt**, **ms_updatedAt**, and **ms_version** are optional system properties. >[AZURE.NOTE] The Mobile Services SDK reserves column names that begin with "**`ms_`**". Do not use this prefix on anything other than system columns. Otherwise, your column names will be modified when using the remote service. - When using the offline sync feature, you must define the system tables as shown below. ### System Tables #### MS_TableOperations | Attribute | Type | |-------------- | ------ | | id (required) | Integer 64 | | itemId | String | | properties | Binary Data | | table | String | | tableKind | Integer 16 | #### MS_TableOperationErrors | Attribute | Type | |-------------- | ---------- | | id (required) | String | | operationId | Integer 64 | | properties | Binary Data | | tableKind | Integer 16 | #### MS_TableConfig | Attribute | Type | |-------------- | ---------- | | id (required) | String | | key | String | | keyType | Integer 64 | | table | String | | value | String | ### Data Table #### TodoItem | Attribute | Type | Note | |-------------- | ------ | -------------------------------------------------------| | id (required) | String | primary key in remote store (required) | | complete | Boolean | todo item field | | text | String | todo item field | | ms_createdAt | Date | (optional) maps to __createdAt system property | | ms_updatedAt | Date | (optional) maps to __updatedAt system property | | ms_version | String | (optional) used to detect conflicts, maps to __version | ## Change Sync Behavior of App In this section, you modify the app so that it does not sync on app start, or when inserting and updating items, but only when the refresh gesture is performed. * In **QSTodoListViewController.m**, change `viewDidLoad` to remove the call to `[self refresh]` at the end of the method. Now, the data will not be synced with the server on app start, but instead will be only stored locally. * In **QSTodoService.m**, modify `addItem` so that it doesn't sync after the item is inserted. Remove the `self syncData` block and replace it with the following: ``` if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); } ``` * Similarly, again in **QSTodoService.m**, in `completeItem`, remove the block for `self syncData` and replace with the following: ``` if (completion != nil) { dispatch_async(dispatch_get_main_queue(), completion); } ``` ## Test App In this section, you will turn of Wi-Fi in the simulator to create an offline scenario. When you add data items, they will be held in the local Core Data store, but not synced to the mobile service. 1. Turn off the internet connection on your Mac. Turning off WiFi in just iOS simulator may not have an effect, since the simulator may still use the host Mac's internet connection, so turn off internet for the computer itself. This simulates an offline scenario. 2. Add some todo items or complete some items. Quit the simulator (or forcibly close the app) and restart. Verify that your changes have been persisted. Notice that the data items are still displayed because they are held in the local Core Data store. 3. View the contents of the remote TodoItem table. Verify that the new items have _not_ been synced to the server. - For the JavaScript backend, go to the [Azure classic portal](http://manage.windowsazure.com), and click the Data tab to view the contents of the `TodoItem` table. - For the .NET backend, view the table contents either with a SQL tool such as SQL Server Management Studio, or a REST client such as Fiddler or Postman. 4. Turn on Wi-Fi in the iOS simulator. Next, perform the refresh gesture by pulling down the list of items. You will see a progress spinner and the text "Syncing...". 5. View the TodoItem data again. The new and changed TodoItems should now appear. ## Summary In order to support the offline features of mobile services, you used the `MSSyncTable` interface and initialized `MSClient.syncContext` with a local store. In this case the local store was a Core Data-based database. When using a Core Data local store, you define several tables with the [correct system properties][Review the Core Data model]. The normal operations for mobile services work as if the app is still connected but all the operations occur against the local store. To synchronize the local store with the server, you used `MSSyncTable.pullWithQuery` and `MSClient.syncContext.pushWithCompletion`: * To push changes to the server, you called `pushWithCompletion`. This method is in `MSSyncContext` instead of the sync table because it will push changes across all tables. Only records that are modified in some way locally (through CUD operations) are be sent to the server. * To pull data from a table on the server to the app, you called `MSSyncTable.pullWithQuery`. A pull always issues a push first. This is to ensure all tables in the local store along with relationships remain consistent. `pullWithQuery` can also be used to filter the data that is stored on the client, by customizing the `query` parameter. ## Next Steps * [Handling Conflicts with Offline Support for Mobile Services] * [Using Soft Delete in Mobile Services][Soft Delete] ## Additional Resources * [Cloud Cover: Offline Sync in Azure Mobile Services] * [Azure Friday: Offline-enabled apps in Azure Mobile Services] \(note: demos are for Windows, but feature discussion applies to all platforms\) [Get the sample app]: #get-app [Review the Core Data model]: #review-core-data [Review the Mobile Services sync code]: #review-sync [Change the sync behavior of the app]: #setup-sync [Test the app]: #test-app [core-data-1]: ./media/mobile-services-ios-get-started-offline-data/core-data-1.png [core-data-2]: ./media/mobile-services-ios-get-started-offline-data/core-data-2.png [core-data-3]: ./media/mobile-services-ios-get-started-offline-data/core-data-3.png [defining-core-data-main-screen]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-main-screen.png [defining-core-data-model-editor]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-model-editor.png [defining-core-data-tableoperationerrors-entity]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-tableoperationerrors-entity.png [defining-core-data-tableoperations-entity]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-tableoperations-entity.png [defining-core-data-tableconfig-entity]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-tableconfig-entity.png [defining-core-data-todoitem-entity]: ./media/mobile-services-ios-get-started-offline-data/defining-core-data-todoitem-entity.png [update-framework-1]: ./media/mobile-services-ios-get-started-offline-data/update-framework-1.png [update-framework-2]: ./media/mobile-services-ios-get-started-offline-data/update-framework-2.png [Core Data Model Editor Help]: https://developer.apple.com/library/mac/recipes/xcode_help-core_data_modeling_tool/Articles/about_cd_modeling_tool.html [Creating an Outlet Connection]: https://developer.apple.com/library/mac/recipes/xcode_help-interface_builder/articles-connections_bindings/CreatingOutlet.html [Build a User Interface]: https://developer.apple.com/library/mac/documentation/ToolsLanguages/Conceptual/Xcode_Overview/Edit_User_Interfaces/edit_user_interface.html [Adding a Segue Between Scenes in a Storyboard]: https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardSegue.html#//apple_ref/doc/uid/TP40014225-CH25-SW1 [Adding a Scene to a Storyboard]: https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardScene.html [Core Data]: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html [Download the preview SDK here]: http://aka.ms/Gc6fex [How to use the Mobile Services client library for iOS]: mobile-services-ios-how-to-use-client-library.md [Offline iOS Sample]: https://github.com/Azure/mobile-services-samples/tree/master/TodoOffline/iOS/blog20140611 [Mobile Services sample repository on GitHub]: https://github.com/Azure/mobile-services-samples [Get started with Mobile Services]: mobile-services-ios-get-started.md [Handling Conflicts with Offline Support for Mobile Services]: mobile-services-ios-handling-conflicts-offline-data.md [Soft Delete]: mobile-services-using-soft-delete.md [Cloud Cover: Offline Sync in Azure Mobile Services]: http://channel9.msdn.com/Shows/Cloud+Cover/Episode-155-Offline-Storage-with-Donna-Malayeri [Azure Friday: Offline-enabled apps in Azure Mobile Services]: http://azure.microsoft.com/documentation/videos/azure-mobile-services-offline-enabled-apps-with-donna-malayeri/ [Mobile Services Quick Start tutorial]: mobile-services-ios-get-started.md ================================================ FILE: docs/mobile-services-ios-get-started-users.md ================================================ # Add Authentication to Existing App > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). In this tutorial, you add authentication to the [Mobile Services Quick Start tutorial] using a supported identity provider. We recommend that you complete the [Mobile Services Quick Start tutorial] first. Alternatively, just download the Quick Start iOS project from the [Azure classic portal] click **Mobile Services** > your mobile service > the cloud sign on top left > **iOS** > **Create a New iOS App** > **Download and run your app** > **Objective-C** > **Download**. Remember to click **Create TodoItem Table** before you click **Download**, if you haven't already created the table. ## Register App for Authentication 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict Data Permissions to Authenticated Users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario. ## Add Authentication to App * Open **QSTodoListViewController.m** and add the following method. Change _facebook_ to _microsoftaccount_, _twitter_, _google_, or _windowsazureactivedirectory_ if you're not using Facebook as your identity provider. ``` - (void) loginAndGetData { MSClient *client = self.todoService.client; if (client.currentUser != nil) { return; } [client loginWithProvider:@"facebook" controller:self animated:YES completion:^(MSUser *user, NSError *error) { [self refresh]; }]; } ``` * Replace `[self refresh]` in `viewDidLoad` with the following: ``` [self loginAndGetData]; ``` * Press **Run** to start the app, and then log in. When you are logged in, you should be able to view the Todo list and make updates. ## Store Authentication Tokens in App The previous example contacts both the identity provider and the mobile service every time the app starts. Instead, you can cache the authorization token and try to use it first. * The recommended way to encrypt and store authentication tokens on an iOS client is use the iOS Keychain. We'll use [SSKeychain](https://github.com/soffes/sskeychain) -- a simple wrapper around the iOS Keychain. Follow the instructions on the SSKeychain page and add it to your project. Verify that the **Enable Modules** setting is enabled in the project's **Build Settings** (section **Apple LLVM - Languages - Modules**.) * Open **QSTodoListViewController.m** and add the following code: ``` - (void) saveAuthInfo { [SSKeychain setPassword:self.todoService.client.currentUser.mobileServiceAuthenticationToken forService:@"AzureMobileServiceTutorial" account:self.todoService.client.currentUser.userId] } - (void)loadAuthInfo { NSString *userid = [[SSKeychain accountsForService:@"AzureMobileServiceTutorial"][0] valueForKey:@"acct"]; if (userid) { NSLog(@"userid: %@", userid); self.todoService.client.currentUser = [[MSUser alloc] initWithUserId:userid]; self.todoService.client.currentUser.mobileServiceAuthenticationToken = [SSKeychain passwordForService:@"AzureMobileServiceTutorial" account:userid]; } } ``` * In `loginAndGetData`, modify `loginWithProvider:controller:animated:completion:`'s completion block. Add the following line right before `[self refresh]` to store the user ID and token properties: ``` [self saveAuthInfo]; ``` * Let's load the user ID and token when the app starts. In the `viewDidLoad` in **QSTodoListViewController.m**, add this right after`self.todoService` is initialized. ``` [self loadAuthInfo]; ``` ## Next Steps Next, learn [how to use the user ID value to filter returned data](mobile-services-javascript-backend-service-side-authorization.md). [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [Storing authentication tokens in your app]:#store-authentication [4]: ./media/mobile-services-ios-get-started-users/mobile-services-selection.png [5]: ./media/mobile-services-ios-get-started-users/mobile-service-uri.png [13]: ./media/mobile-services-ios-get-started-users/mobile-identity-tab.png [14]: ./media/mobile-services-ios-get-started-users/mobile-portal-data-tables.png [15]: ./media/mobile-services-ios-get-started-users/mobile-portal-change-table-perms.png [Service-side authorization of Mobile Services users]: mobile-services-javascript-backend-service-side-authorization.md [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Single sign-on for Windows Store apps by using Live Connect]: https://azure.microsoft.com/develop/mobile/tutorials/single-sign-on-windows-8-dotnet [Mobile Services Quick Start tutorial]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-ios [Get started with data]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-data-ios [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-ios [Get started with push notifications]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-ios [Authorize users with scripts]: https://azure.microsoft.com/develop/mobile/tutorials/authorize-users-in-scripts-ios [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-ios-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to an iOS app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple _To do list_ app that stores app data in the new mobile service. The mobile service that you will create uses JavaScript for server-side business logic. To create a mobile service with server-side business logic in .NET, see the [.NET backend version] of this topic. > [AZURE.NOTE] To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get [free mobile services that you can keep using even after your trial ends](https://azure.microsoft.com/pricing/details/mobile-services/). For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=AE564AB28&returnurl=http%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fdevelop%2Fmobile%2Ftutorials%2Fget-started-ios%2F%20). ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new iOS app You can follow an easy Quick Start in the Azure classic portal to create a new app connected to your mobile service: 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the Quick Start tab, click **iOS** under **Choose a platform** and expand **Create a new iOS app**. This displays the steps to create an iOS app connected to your mobile service. 3. Click **Create TodoItem table** to create a table to store app data. 4. Under **Download and run your app**, click **Download**. This downloads the project for the sample _To do list_ application that is connected to your mobile service, along with the Mobile Services iOS SDK. Save the compressed project file to your local computer, and make a note of where you saved it. ## Run your new iOS app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files, expand the files on your computer, and open the project file using Xcode. 2. Press the **Run** button to build the project and start the app in the iPhone emulator. 3. In the app, type meaningful text, such as _Complete the tutorial_ and then click the plus (**+**) icon. ![](./media/mobile-services-ios-run-app/mobile-quickstart-startup-ios.png) This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list.
  1. Back in the [Azure classic portal], click the **DATA** tab and then click the **TodoItem** table. This lets you browse the data inserted by the app into the table.

## Next Steps Learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync]
Learn how to use offline data sync to make your app responsive and robust. * [Add authentication to an existing app]
Learn how to authenticate users of your app with an identity provider. * [Add push notifications to an existing app]
Learn how to send a very basic push notification to your app. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [6]: ./media/mobile-services-ios-get-started/mobile-portal-quickstart-ios.png [7]: ./media/mobile-services-ios-get-started/mobile-quickstart-steps-ios.png [8]: ./media/mobile-services-ios-get-started/mobile-xcode-project.png [10]: ./media/mobile-services-ios-get-started/mobile-quickstart-startup-ios.png [11]: ./media/mobile-services-ios-get-started/mobile-data-tab.png [12]: ./media/mobile-services-ios-get-started/mobile-data-browse.png [Get started with offline data sync]: mobile-services-ios-get-started-offline-data.md [Add authentication to an existing app]: mobile-services-dotnet-backend-ios-get-started-users.md [Add push notifications to an existing app]: mobile-services-dotnet-backend-ios-get-started-push.md [Mobile Services iOS SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Azure classic portal]: https://manage.windowsazure.com/ [XCode]: https://go.microsoft.com/fwLink/p/?LinkID=266532 [.NET backend version]: mobile-services-dotnet-backend-ios-get-started.md ================================================ FILE: docs/mobile-services-ios-handling-conflicts-offline-data.md ================================================ # Handling Conflicts with Offline Data Sync in Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(iOS | Any)](mobile-services-ios-handling-conflicts-offline-data.md) - [(Windows Runtime 8.1 universal C# | Any)](mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to synchronize data and handle conflicts when using the offline capabilities of Azure Mobile Services. This tutorial builds on the [Get Started with Offline Data] tutorial. >[AZURE.NOTE] To complete this tutorial, you need a Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see Azure Free Trial. ## Download iOS Project For this tutorial, download [an updated Xcode project from Github](https://github.com/Azure/mobile-services-samples/tree/master/TodoOffline/iOS). We've used the Xcode project from the end of [Get Started with Offline Data] tutorial as a starting point, and then updated it to allow the editing of items. We've also added supporting classes and methods so we can add a conflict handler in the next section. At the end of this tutorial, if you you run this app on two phones, change the same item on both phones locally, and push the changes back to the server, you'll allow the user on each phone to choose which version to keep: * keep the client version (which overrides the version on the server), * keep the server version (which updates the client local table), or * keep neither version (cancels push and leaves the operation pending). Now, let's add the conflict handler to enable this capability. ## Add Conflict Handler to Todo List View Controller 1. In **QSTodoListViewController.m**, edit **viewDidLoad**. Replace the call to **defaultService** with a call to **defaultServiceWithDelegate** instead: self.todoService = [QSTodoService defaultServiceWithDelegate:self]; 2. In **QSTodoListViewController.h**, add **<MSSyncContextDelegate>** to the interface declaration so we're implementing the **MSSyncContextDelegate** protocol. @interface QSTodoListViewController : UITableViewController 3. Add the following import statement at the top of **QSTodoListViewController.m**: #import "QSUIAlertViewWithBlock.h" 4. Finally, let's add the following two operations to **QSTodoListViewController.m** to use this helper class and prompt the user to reconcile the conflict in one of three ways. - (void)tableOperation:(MSTableOperation *)operation onComplete:(MSSyncItemBlock)completion { [self doOperation:operation complete:completion]; } -(void)doOperation:(MSTableOperation *)operation complete:(MSSyncItemBlock)completion { [operation executeWithCompletion:^(NSDictionary *item, NSError *error) { NSDictionary *serverItem = [error.userInfo objectForKey:MSErrorServerItemKey]; if (error.code == MSErrorPreconditionFailed) { QSUIAlertViewWithBlock *alert = [[QSUIAlertViewWithBlock alloc] initWithCallback:^(NSInteger buttonIndex) { if (buttonIndex == 1) { // Client NSMutableDictionary *adjustedItem = [operation.item mutableCopy]; [adjustedItem setValue:[serverItem objectForKey:MSSystemColumnVersion] forKey:MSSystemColumnVersion]; operation.item = adjustedItem; [self doOperation:operation complete:completion]; return; } else if (buttonIndex == 2) { // Server NSDictionary *serverItem = [error.userInfo objectForKey:MSErrorServerItemKey]; completion(serverItem, nil); } else { // Cancel [operation cancelPush]; completion(nil, error); } }]; NSString *message = [NSString stringWithFormat:@"Client value: %@\nServer value: %@", operation.item[@"text"], serverItem[@"text"]]; [alert showAlertWithTitle:@"Server Conflict" message:message cancelButtonTitle:@"Cancel" otherButtonTitles:[NSArray arrayWithObjects:@"Use Client", @"Use Server", nil]]; } else { completion(item, error); } }]; } ## Test the App Let's test the application with conflicts! Edit the same item in two different instances of the app running at the same time, or using the app and a REST client. Perform the refresh gesture in the app instances by dragging from the top. Now you'll see a prompt to reconcile the conflict: ![][conflict-ui] [Update the App Project to Allow Editing]: #update-app [Update Todo List View Controller]: #update-list-view [Add Todo Item View Controller]: #add-view-controller [Add Todo Item View Controller and Segue to Storyboard]: #add-segue [Add Item Details to Todo Item View Controller]: #add-item-details [Add Support for Saving Edits]: #saving-edits [Conflict Handling Problem]: #conflict-handling-problem [Update QSTodoService to Support Conflict Handling]: #service-add-conflict-handling [Add UI Alert View Helper to Support Conflict Handling]: #add-alert-view [Add Conflict Handler to Todo List View Controller]: #add-conflict-handling [Test the App]: #test-app [add-todo-item-view-controller-3]: ./media/mobile-services-ios-handling-conflicts-offline-data/add-todo-item-view-controller-3.png [add-todo-item-view-controller-4]: ./media/mobile-services-ios-handling-conflicts-offline-data/add-todo-item-view-controller-4.png [add-todo-item-view-controller-5]: ./media/mobile-services-ios-handling-conflicts-offline-data/add-todo-item-view-controller-5.png [add-todo-item-view-controller-6]: ./media/mobile-services-ios-handling-conflicts-offline-data/add-todo-item-view-controller-6.png [todo-list-view-controller-add-segue]: ./media/mobile-services-ios-handling-conflicts-offline-data/todo-list-view-controller-add-segue.png [update-todo-list-view-controller-2]: ./media/mobile-services-ios-handling-conflicts-offline-data/update-todo-list-view-controller-2.png [conflict-handling-problem-1]: ./media/mobile-services-ios-handling-conflicts-offline-data/conflict-handling-problem-1.png [conflict-ui]: ./media/mobile-services-ios-handling-conflicts-offline-data/conflict-ui.png [Segmented Controls]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/UIKitUICatalog/UISegmentedControl.html [Core Data Model Editor Help]: https://developer.apple.com/library/mac/recipes/xcode_help-core_data_modeling_tool/Articles/about_cd_modeling_tool.html [Creating an Outlet Connection]: https://developer.apple.com/library/mac/recipes/xcode_help-interface_builder/articles-connections_bindings/CreatingOutlet.html [Build a User Interface]: https://developer.apple.com/library/mac/documentation/ToolsLanguages/Conceptual/Xcode_Overview/Edit_User_Interfaces/edit_user_interface.html [Adding a Segue Between Scenes in a Storyboard]: https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardSegue.html#//apple_ref/doc/uid/TP40014225-CH25-SW1 [Adding a Scene to a Storyboard]: https://developer.apple.com/library/ios/recipes/xcode_help-IB_storyboard/chapters/StoryboardScene.html [Core Data]: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreData/cdProgrammingGuide.html [Download the preview SDK here]: http://aka.ms/Gc6fex [How to use the Mobile Services client library for iOS]: mobile-services-ios-how-to-use-client-library.md [Getting Started Offline iOS Sample]: https://github.com/Azure/mobile-services-samples/tree/master/TodoOffline/iOS/blog20140611 [Get Started with Offline Data]: mobile-services-ios-get-started-offline-data.md [Get started with Mobile Services]: mobile-services-ios-get-started.md ================================================ FILE: docs/mobile-services-ios-how-to-use-client-library.md ================================================ # How to Use iOS Client Library for Azure Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Android](mobile-services-android-how-to-use-client-library.md) - [HTML/JavaScript](mobile-services-html-how-to-use-client-library.md) - [iOS](mobile-services-ios-how-to-use-client-library.md) - [Managed (Windows/Xamarin)](mobile-services-dotnet-how-to-use-client-library.md) This guide teaches you to perform common scenarios using the Azure Mobile Services [iOS SDK]. If you are new to Mobile Services, first complete [Mobile Services Quick Start] to configure your account, create a table, and create a mobile service. > [AZURE.NOTE] This guide uses the latest [iOS Mobile Services SDK](https://go.microsoft.com/fwLink/?LinkID=266533&clcid=0x409). If your project uses an older version of the SDK, first upgrade the framework in Xcode. ## What is Mobile Services Azure Mobile Services is a highly scalable mobile application development platform that lets you add enhanced functionality to your mobile device apps by using Azure. With Mobile Services you can: + **Build native and cross platform apps** - Connect your iOS, Android, Windows, or cross-platform Xamarin or Cordova (Phonegap) apps to your backend mobile service using native SDKs. + **Send push notifications to your users** - Send push notifications to your users of your app. + **Authenticate your users** - Leverage popular identity providers like Facebook and Twitter to authenticate your app users. + **Store data in the cloud** - Store user data in a SQL Database (by default) or in Mongo DB, DocumentDB, Azure Tables, or Azure Blobs. + **Build offline-ready apps with sync** - Make your apps work offline and use Mobile Services to sync data in the background. + **Monitor and scale your apps** - Monitor app usage and scale your backend as demand grows. ## Mobile Services Concepts The following are important features and concepts in the Mobile Services: + **Application key:** a unique value that is used to limit access to your mobile service from random clients; this "key" is not a security token and is not used to authenticate users of your app. + **Backend:** the mobile service instance that supports your app. A mobile service is implemented either as an ASP.NET Web API project (*.NET backend* ) or as a Node.js project (*JavaScript backend*). + **Identity provider:** an external service, trusted by Mobile Services, that authenticates your app's users. Supported providers include: Facebook, Twitter, Google, Microsoft Account, and Azure Active Directory. + **Push notification:** Service-initiated message that is sent to a registered device or user using Azure Notification Hubs. + **Scale:** The ability to add, for an additional cost, more processing power, performance, and storage as your app becomes more popular. + **Scheduled job:** Custom code that is run either on a pre-determined schedule or on-demand. For more information, see [Mobile Services Concepts](./ mobile-services-concepts-links.md). ## Setup and Prerequisites This guide assumes that you have created a mobile service with a table. For more information see [Create a table], or reuse the `TodoItem` table created in [Mobile Services Quick Start]. This guide assumes that the table has the same schema as the tables in those tutorials. This guide also assumes that your Xcode references `WindowsAzureMobileServices.framework` and imports `WindowsAzureMobileServices/WindowsAzureMobileServices.h`. ## How to: Create Mobile Services Client To access an Azure mobile service in your project, create an `MSClient` client object. Replace `AppUrl` and `AppKey` with the mobile service URL and the application key Dashboard values, respectively. ``` MSClient *client = [MSClient clientWithApplicationURLString:@"AppUrl" applicationKey:@"AppKey"]; ``` ## How to: Create Table Reference To access or update data for your Azure mobile service, create a reference to the table. Replace `TodoItem` with the name of your table. ``` MSTable *table = [client tableWithName:@"TodoItem"]; ``` ## How to: Query Data To create a database query, query the `MSTable` object. The following query gets all the items in `TodoItem` and logs the text of each item. ``` [table readWithCompletion:^(MSQueryResult *result, NSError *error) { if(error) { // error is nil if no error occured NSLog(@"ERROR %@", error); } else { for(NSDictionary *item in result.items) { // items is NSArray of records that match query NSLog(@"Todo Item: %@", [item objectForKey:@"text"]); } } }]; ``` ## How to: Filter Returned Data To filter results, there are many available options. To filter using a predicate, use an `NSPredicate` and `readWithPredicate`. The following filters returned data to find only incomplete Todo items. ``` // Create a predicate that finds items where complete is false NSPredicate * predicate = [NSPredicate predicateWithFormat:@"complete == NO"]; // Query the TodoItem table and update the items property with the results from the service [table readWithPredicate:predicate completion:^(MSQueryResult *result, NSError *error) { if(error) { NSLog(@"ERROR %@", error); } else { for(NSDictionary *item in result.items) { NSLog(@"Todo Item: %@", [item objectForKey:@"text"]); } } }]; ``` ## How to: Use MSQuery To perform a complex query (including sorting and paging), create an `MSQuery` object, directly or by using a predicate: ``` MSQuery *query = [table query]; MSQuery *query = [table queryWithPredicate: [NSPredicate predicateWithFormat:@"complete == NO"]]; ``` `MSQuery` lets you control several query behaviors, including the following. Execute an `MSQuery` query by calling `readWithCompletion` on it, as shown in the next example. * Specify order of results * Limit which fields to return * Limit how many records to return * Specify total count in response * Specify custom query string parameters in request * Apply additional functions ## How to: Sort Data with MSQuery To sort results, let's look at an example. To first ascendingly by field `text` and then descendingly by field `completion`, invoke `MSQuery` like so: ``` [query orderByAscending:@"text"]; [query orderByDescending:@"complete"]; [query readWithCompletion:^(MSQueryResult *result, NSError *error) { if(error) { NSLog(@"ERROR %@", error); } else { for(NSDictionary *item in result.items) { NSLog(@"Todo Item: %@", [item objectForKey:@"text"]); } } }]; ``` ## How to: Return Data in Pages with MSQuery Mobile Services limits the amount of records that are returned in a single response. To control the number of records displayed to your users you must implement a paging system. Paging is performed by using the following three properties of the **MSQuery** object: ``` + `BOOL includeTotalCount` + `NSInteger fetchLimit` + `NSInteger fetchOffset` ``` In the following example, a simple function requests 5 records from the server and then appends them to the local collection of previously loaded records: ``` // Create and initialize these properties @property (nonatomic, strong) NSMutableArray *loadedItems; // Init via [[NSMutableArray alloc] init] @property (nonatomic) BOOL moreResults; ``` ``` -(void)loadResults { MSQuery *query = [self.table query]; query.includeTotalCount = YES; query.fetchLimit = 5; query.fetchOffset = self.loadedItems.count; [query readWithCompletion:^(MSQueryResult *result, NSError *error) { if(!error) { // Add the items to our local copy [self.loadedItems addObjectsFromArray:result.items]; // Set a flag to keep track if there are any additional records we need to load self.moreResults = (self.loadedItems.count <= result.totalCount); } }]; } ``` ## How to: Limit Fields and Expand Query String Parameters with MSQuery To limit fields to be returned in a query, specify the names of the fields in the **selectFields** property. This returns only the text and completed fields: ``` query.selectFields = @[@"text", @"completed"]; ``` To include additional query string parameters in the server request (for example, because a custom server-side script uses them), populate `query.parameters` like so: ``` query.parameters = @{ @"myKey1" : @"value1", @"myKey2" : @"value2", }; ``` ## How to: Insert Data To insert a new table row, create a new `NSDictionary` and invoke `table insert`. Mobile Services automatically generates new columns based on the `NSDictionary` if [Dynamic Schema] is not disabled. If `id` is not provided, the backend automatically generates a new unique ID. Provide your own `id` to use email addresses, usernames, or your own custom values as ID. Providing your own ID may ease joins and business-oriented database logic. ``` NSDictionary *newItem = @{@"id": @"custom-id", @"text": @"my new item", @"complete" : @NO}; [self.table insert:newItem completion:^(NSDictionary *result, NSError *error) { // The result contains the new item that was inserted, // depending on your server scripts it may have additional or modified // data compared to what was passed to the server. if(error) { NSLog(@"ERROR %@", error); } else { NSLog(@"Todo Item: %@", [result objectForKey:@"text"]); } }]; ``` ## How to: Modify Data To update an existing row, modify an item and call `update`: ``` NSMutableDictionary *newItem = [oldItem mutableCopy]; // oldItem is NSDictionary [newItem setValue:@"Updated text" forKey:@"text"]; [self.table update:newItem completion:^(NSDictionary *item, NSError *error) { // Handle error or perform additional logic as needed }]; ``` Alternatively, supply the row ID and the updated field: ``` [self.table update:@{@"id":@"37BBF396-11F0-4B39-85C8-B319C729AF6D", @"Complete":@YES} completion:^(NSDictionary *item, NSError *error) { // Handle error or perform additional logic as needed }]; ``` At minimum, the `id` attribute must be set when making updates. ## How to: Delete Data To delete an item, invoke `delete` with the item: ``` [self.table delete:item completion:^(id itemId, NSError *error) { // Handle error or perform additional logic as needed }]; ``` Alternatively, delete by providing a row ID: ``` [self.table deleteWithId:@"37BBF396-11F0-4B39-85C8-B319C729AF6D" completion:^(id itemId, NSError *error) { // Handle error or perform additional logic as needed }]; ``` At minimum, the `id` attribute must be set when making deletes. ## How to: Call a custom API A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON. For an example of how to create a custom API in your mobile service, see [How to: define a custom API endpoint](mobile-services-dotnet-backend-define-custom-api.md). ### Call custom API from iOS app To call this custom API from an iOS client, use the `MSClient invokeAPI` method. There are two versions of this method, one for JSON-formatted requests, and one for any data type: /// Invokes a user-defined API of the Mobile Service. The HTTP request and /// response content will be treated as JSON. -(void)invokeAPI:(NSString *)APIName body:(id)body HTTPMethod:(NSString *)method parameters:(NSDictionary *)parameters headers:(NSDictionary *)headers completion:(MSAPIBlock)completion; /// Invokes a user-defined API of the Mobile Service. The HTTP request and /// response content can be of any media type. -(void)invokeAPI:(NSString *)APIName data:(NSData *)data HTTPMethod:(NSString *)method parameters:(NSDictionary *)parameters headers:(NSDictionary *)headers completion:(MSAPIDataBlock)completion; For example, to send a JSON request to a custom API named "sendEmail", pass an `NSDictionary` for the request parameters: NSDictionary *emailHeader = @{ @"to": @"email.com", @"subject" : @"value" }; [self.client invokeAPI:@"sendEmail" body:emailBody HTTPMethod:@"POST" parameters:emailHeader headers:nil completion:completion ]; If you need the data returned then you can use something like this: [self.client invokeAPI:apiName body:yourBody HTTPMethod:httpMethod parameters:parameters headers:headers completion: ^(NSData *result, NSHTTPURLResponse *response, NSError *error){ // error is nil if no error occured if(error) { NSLog(@"ERROR %@", error); } else { // do something with the result } }]; ## How to: Authenticate Users Azure Mobile Services supports various identity providers. For a basic tutorial, see [Authentication]. Azure Mobile Services supports two authentication workflows: - **Server-managed Login**: Azure Mobile Services manages the login process on behalf of your app. It displays a provider-specific login page and authenticates with the chosen provider. - **Client-managed Login**: The _app_ requests a token from the identity provider and presents this token to Azure Mobile Services for authentication. When authentication succeeds, you get back a user object with a user ID value and the auth token. To use this user ID to authorize users, see [Service-side Authorization]. To restrict table access to only authenticated users, see [Permissions]. ### Server-managed Login Here is how you can add server-managed login to the [Mobile Services Quick Start] project; you may use similar code for your other projects. For more information and to see an end-to-end example in action, see [Authentication]. * Open **QSTodoListViewController.m** and add the following method. Change _facebook_ to _microsoftaccount_, _twitter_, _google_, or _windowsazureactivedirectory_ if you're not using Facebook as your identity provider. ``` - (void) loginAndGetData { MSClient *client = self.todoService.client; if (client.currentUser != nil) { return; } [client loginWithProvider:@"facebook" controller:self animated:YES completion:^(MSUser *user, NSError *error) { [self refresh]; }]; } ``` * Replace `[self refresh]` in `viewDidLoad` with the following: ``` [self loginAndGetData]; ``` * Press **Run** to start the app, and then log in. When you are logged in, you should be able to view the Todo list and make updates. ### Client-managed Login (Single Sign-on) You may do the login process outside the Mobile Services client, either to enable single sign-on or if your app contacts the identity provider directly. In such cases, you can log in to Mobile Services by providing a token obtained independently from a supported identity provider. The following example uses the [Live Connect SDK] to enable single sign-on for iOS apps. It assumes that you have a **LiveConnectClient** instance named `liveClient` in the controller and the user is logged in. ``` [client loginWithProvider:@"microsoftaccount" token:@{@"authenticationToken" : self.liveClient.session.authenticationToken} completion:^(MSUser *user, NSError *error) { // Handle success and errors }]; ``` ## How to: Cache Authentication Tokens Let's see how you may cache tokens in the [Mobile Services Quick Start] project; you may apply similar steps to any project. * The recommended way to encrypt and store authentication tokens on an iOS client is use the iOS Keychain. We'll use [SSKeychain](https://github.com/soffes/sskeychain) -- a simple wrapper around the iOS Keychain. Follow the instructions on the SSKeychain page and add it to your project. Verify that the **Enable Modules** setting is enabled in the project's **Build Settings** (section **Apple LLVM - Languages - Modules**.) ## How to: Handle Errors When you call a mobile service, the completion block contains an `NSError *error` parameter. When an error occurs, this parameter is non-nil. In your code, you should check this parameter and handle the error as needed. The file [``](https://github.com/Azure/azure-mobile-services/blob/master/sdk/iOS/src/MSError.h) defines the constants `MSErrorResponseKey`, `MSErrorRequestKey`, and `MSErrorServerItemKey` to get more data related to the error. In addition, the file defines constants for each error code. For an example on how to use these constants, see [Conflict-Handler] for its usage of `MSErrorServerItemKey` and `MSErrorPreconditionFailed`. [What is Mobile Services]: #what-is [Concepts]: #concepts [Setup and Prerequisites]: #Setup [How to: Create the Mobile Services client]: #create-client [How to: Create a table reference]: #table-reference [How to: Query data from a mobile service]: #querying [Filter returned data]: #filtering [Sort returned data]: #sorting [Return data in pages]: #paging [Select specific columns]: #selecting [How to: Bind data to the user interface]: #binding [How to: Insert data into a mobile service]: #inserting [How to: Modify data in a mobile service]: #modifying [How to: Authenticate users]: #authentication [Cache authentication tokens]: #caching-tokens [How to: Upload images and large files]: #blobs [How to: Handle errors]: #errors [How to: Design unit tests]: #unit-testing [How to: Customize the client]: #customizing [Customize request headers]: #custom-headers [Customize data type serialization]: #custom-serialization [Next Steps]: #next-steps [How to: Use MSQuery]: #query-object [Mobile Services Quick Start]: mobile-services-ios-get-started.md [Get started with Mobile Services]: mobile-services-ios-get-started.md [Mobile Services SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-ios [iOS SDK]: https://developer.apple.com/xcode [Handling Expired Tokens]: http://go.microsoft.com/fwlink/p/?LinkId=301955 [Live Connect SDK]: http://go.microsoft.com/fwlink/p/?LinkId=301960 [Permissions]: http://msdn.microsoft.com/library/windowsazure/jj193161.aspx [Service-side Authorization]: mobile-services-javascript-backend-service-side-authorization.md [Dynamic Schema]: http://go.microsoft.com/fwlink/p/?LinkId=296271 [Create a table]: http://msdn.microsoft.com/library/windowsazure/jj193162.aspx [NSDictionary object]: http://go.microsoft.com/fwlink/p/?LinkId=301965 [ASCII control codes C0 and C1]: http://en.wikipedia.org/wiki/Data_link_escape_character#C1_set [CLI to manage Mobile Services tables]: https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-command-line-tools/#Mobile_Tables [Conflict-Handler]: mobile-services-ios-handling-conflicts-offline-data.md#add-conflict-handling ================================================ FILE: docs/mobile-services-javascript-backend-android-get-started-push.md ================================================ # Add push notifications to your Mobile Services Android app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Summary This topic shows how to use Azure Mobile Services to send push notifications to your Android app using Google Cloud Messaging ("GCM"). You will add push notifications to the quickstart project that is a prerequisite for this tutorial. Push notifications are enabled by using the Azure Notification Hub that is included in your mobile service. When complete, your mobile service will send a push notification each time a record is inserted. ## Prerequisites This tutorial is based on the code you download in the Mobile Services quickstart. Before you start this tutorial, you must first complete either [Get started with Mobile Services] or [Add Mobile Services to an existing app]. > [AZURE.IMPORTANT] If you completed the quickstart tutorial prior to the release of Azure Mobile Services Android SDK 2.0, you must re-do it, because the SDK is not backwards compatible. To verify the version, check the **dependencies** section of your project's **build.gradle** file. ## Sample code To see the completed source code go [here](https://github.com/Azure/mobile-services-samples/tree/master/GettingStartedWithPush). ## Enable Google Cloud Messaging 1. Navigate to the [Google Cloud Console](https://console.developers.google.com/project), sign in with your Google account credentials. 2. Click **Create Project**, type a project name, then click **Create**. If requested, carry out the SMS Verification, and click **Create** again. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-new-project.png) Type in your new **Project name** and click **Create project**. 3. Click the **Utilities and More** button and then click **Project Information**. Make a note of the **Project Number**. You will need to set this value as the `SenderId` variable in the client app. ![](./media/mobile-services-enable-google-cloud-messaging/notification-hubs-utilities-and-more.png) 4. In the project dashboard, under **Mobile APIs**, click **Google Cloud Messaging**, then on the next page click **Enable API** and accept the terms of service. ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-GCM.png) ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-gcm-2.png) 5. In the project dashboard, Click **Credentials** > **Create Credential** > **API Key**. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-create-server-key.png) 6. In **Create a new key**, click **Server key**, type a name for your key, then click **Create**. 7. Make a note of the **API KEY** value. You will use this API key value to enable Azure to authenticate with GCM and send push notifications on behalf of your app. ## Configure Mobile Services to send push requests 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your app. 2. Click the **Push** tab, enter the **API Key** value obtained from GCM in the previous procedure, then click **Save**. ![](./media/mobile-services-android-configure-push/mobile-push-tab-android.png) >[AZURE.NOTE]When you set your GCM credentials for enhanced push notifications in the Push tab in the portal, they are shared with Notification Hubs to configure the notification hub with your app. Both your mobile service and your app are now configured to work with GCM and Notification Hubs. ## Add push notifications to your app Your next step is to install Google Play services. Google Cloud Messaging has some minimum API level requirements for development and testing, which the **minSdkVersion** property in the Manifest must conform to. If you will be testing with an older device, then consult [Set Up Google Play Services SDK] to determine how low you can set this value, and set it appropriately. ### Add Google Play Services to the project 1. Open the Android SDK Manager by clicking the icon on the toolbar of Android Studio or by clicking **Tools** -> **Android** -> **SDK Manager** on the menu. Locate the target version of the Android SDK that is used in your project , open it, and choose **Google APIs**, if it is not already installed. 2. Scroll down to **Extras**, expand it, and choose **Google Play Services**, as shown below. Click **Install Packages**. Note the SDK path, for use in the following step. ![](./media/notification-hubs-android-get-started/notification-hub-create-android-app4.png) 3. Open the **build.gradle** file in the app directory. ![](./media/mobile-services-android-get-started-push/android-studio-push-build-gradle.png) 4. Add this line under *dependencies*: compile 'com.google.android.gms:play-services-gcm:8.4.0' 5. Under *defaultConfig*, change *minSdkVersion* to 9. 6. Click the **Sync Project with Gradle Files** icon in the tool bar. 7. Open **AndroidManifest.xml** and add this tag to the *application* tag. ### Add code 1. In your **app** project, open the file `AndroidManifest.xml`. In the code in the next two steps, replace _`**my_app_package**`_ with the name of the app package for your project, which is the value of the `package` attribute of the `manifest` tag. 2. Add the following new permissions after the existing `uses-permission` element: 3. Add the following code after the `application` opening tag: 4. Add this line under *dependencies* in the **build.gradle** file in the app directory and re-sync gradle with the project: compile(group: 'com.microsoft.azure', name: 'azure-notifications-handler', version: '1.0.1', ext: 'jar') 5. Open the file *ToDoItemActivity.java*, and add the following import statement: import com.microsoft.windowsazure.notifications.NotificationsManager; 6. Add the following private variable to the class: replace _``_ with the Project Number assigned by Google to your app in the preceding procedure: public static final String SENDER_ID = ""; 7. Change the definition of the *MobileServiceClient* from **private** to **public static**, so it now looks like this: public static MobileServiceClient mClient; 8. Next we need to add a new class to handle notifications. In the Project Explorer, open the **src** => **main** => **java** nodes, and right-click the package name node: click **New**, then click **Java Class**. 9. In **Name** type `MyHandler`, then click **OK**. ![](./media/mobile-services-android-get-started-push/android-studio-create-class.png) 10. In the MyHandler file, replace the class declaration with public class MyHandler extends NotificationsHandler { 11. Add the following import statements for the `MyHandler` class: import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.NotificationCompat; 12. Next add the following members for the `MyHandler` class: public static final int NOTIFICATION_ID = 1; private NotificationManager mNotificationManager; NotificationCompat.Builder builder; Context ctx; 13. In the `MyHandler` class, add the following code to override the **onRegistered** method, which registers your device with the mobile service Notification Hub. @Override public void onRegistered(Context context, final String gcmRegistrationId) { super.onRegistered(context, gcmRegistrationId); new AsyncTask() { protected Void doInBackground(Void... params) { try { ToDoActivity.mClient.getPush().register(gcmRegistrationId, null); return null; } catch(Exception e) { // handle error } return null; } }.execute(); } 14. In the `MyHandler` class, add the following code to override the **onReceive** method, which causes the notification to display when it is received. @Override public void onReceive(Context context, Bundle bundle) { ctx = context; String nhMessage = bundle.getString("message"); sendNotification(nhMessage); } private void sendNotification(String msg) { mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, new Intent(ctx, ToDoActivity.class), 0); NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx) .setSmallIcon(R.drawable.ic_launcher) .setContentTitle("Notification Hub Demo") .setStyle(new NotificationCompat.BigTextStyle() .bigText(msg)) .setContentText(msg); mBuilder.setContentIntent(contentIntent); mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); } 15. Back in the TodoActivity.java file, update the **onCreate** method of the *ToDoActivity* class to register the notification handler class. Make sure to add this code after the *MobileServiceClient* is instantiated. NotificationsManager.handleNotifications(this, SENDER_ID, MyHandler.class); Your app is now updated to support push notifications. [Mobile Services Android SDK]: http://aka.ms/Iajk6q ## Update the registered insert script in the Azure classic portal 1. In the [Azure classic portal](https://manage.windowsazure.com/), click the **Data** tab and then click the **TodoItem** table. 2. In **todoitem**, click the **Script** tab and select **Insert**. This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Define a simple payload for a GCM notification. var payload = { "data": { "message": item.text } }; request.execute({ success: function() { // If the insert succeeds, send a notification. push.gcm.send(null, payload, { success: function(pushResponse) { console.log("Sent push:", pushResponse, payload); request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); request.respond(500, { error: pushResponse }); } }); }, error: function(err) { console.log("request.execute error", err) request.respond(); } }); } This registers a new insert script, which uses the [gcm object](http://go.microsoft.com/fwlink/p/?LinkId=282645) to send a push notification to all registered devices after the insert succeeds. ## Test push notifications in your app You can test the app by directly attaching an Android phone with a USB cable, or by using a virtual device in the emulator. ### Setting up the Android emulator for testing When you run this app in the emulator, make sure that you use an Android Virtual Device (AVD) that supports Google APIs. 1. From the right end of the toolbar, select the Android Virtual Device Manager, select your device, click the edit icon on the right. ![](./media/mobile-services-javascript-backend-android-get-started-push/mobile-services-android-virtual-device-manager.png) 2. Select **Change** on the device description line, select **Google APIs**, then click OK. ![](./media/mobile-services-javascript-backend-android-get-started-push/mobile-services-android-virtual-device-manager-edit.png) This targets the AVD to use Google APIs. ### Running the test 1. From the **Run** menu item, click **Run app** to start the app. 2. In the app, type meaningful text, such as _A new Mobile Services task_ and then click the **Add** button. ![](./media/mobile-services-javascript-backend-android-get-started-push/mobile-quickstart-push1-android.png) 3. Swipe down from the top of the screen to open the device's Notification Drawer to see the notification. You have successfully completed this tutorial. ## Troubleshooting ### Verify Android SDK Version Because of ongoing development, the Android SDK version installed in Android Studio might not match the version in the code. The Android SDK referenced in this tutorial is version 21, the latest at the time of writing. The version number may increase as new releases of the SDK appear, and we recomend using the latest version available. Two symptoms of version mismatch are: 1. When you Build or Rebuild the project, you may get Gradle error messages like "**failed to find target Google Inc.:Google APIs:n**". 2. Standard Android objects in code that should resolve based on `import` statements may be generating error messages. If either of these appear, the version of the Android SDK installed in Android Studio might not match the SDK target of the downloaded project. To verify the version, make the following changes: 1. In Android Studio, click **Tools** => **Android** => **SDK Manager**. If you have not installed the latest version of the SDK Platform, then click to install it. Make a note of the version number. 2. In the Project Explorer tab, under **Gradle Scripts**, open the file **build.gradle (modeule: app)**. Ensure that the **compileSdkVersion** and **buildToolsVersion** are set to the latest SDK version installed. The tags might look like this: compileSdkVersion 'Google Inc.:Google APIs:21' buildToolsVersion "21.1.2" 3. In the Android Studio Project Explorer right-click the project node, choose **Properties**, and in the left column choose **Android**. Ensure that the **Project Build Target** is set to the same SDK version as the **targetSdkVersion**. 4. In Android Studio, the manifest file is no longer used to specify the target SDK and minimum SDK version, unlike the case with Eclipse. ## Next steps Learn more about Mobile Services and Notification Hubs in the following topics: * [Get started with authentication]
Learn how to authenticate users of your app with different account types using mobile services. * [What are Notification Hubs?]
Learn more about how Notification Hubs works to deliver notifications to your apps across all major client platforms. * [Debug Notification Hubs applications](http://go.microsoft.com/fwlink/p/?linkid=386630)
Get guidance troubleshooting and debugging Notification Hubs solutions. * [How to use the Android client library for Mobile Services]
Learn more about how to use Mobile Services with Android. * [Mobile Services server script reference]
Learn more about how to implement business logic in your mobile service. [Register your app for push notifications and configure Mobile Services]: #register [Update the generated push notification code]: #update-scripts [Insert data to receive notifications]: #test [Next Steps]:#next-steps [13]: ./media/mobile-services-windows-store-javascript-get-started-push/mobile-quickstart-push1.png [14]: ./media/mobile-services-windows-store-javascript-get-started-push/mobile-quickstart-push2.png [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Get started with Mobile Services]: mobile-services-android-get-started.md [Get started with authentication]: mobile-services-android-get-started-users.md [Get started with push notifications]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-js [Push notifications to app users]: https://azure.microsoft.com/develop/mobile/tutorials/push-notifications-to-users-js [Authorize users with scripts]: https://azure.microsoft.com/develop/mobile/tutorials/authorize-users-in-scripts-js [JavaScript and HTML]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-js [Set Up Google Play Services SDK]: http://go.microsoft.com/fwlink/?LinkId=389801 [Azure classic portal]: https://manage.windowsazure.com/ [How to use the Android client library for Mobile Services]: mobile-services-android-how-to-use-client-library.md [gcm object]: http://go.microsoft.com/fwlink/p/?LinkId=282645 [Mobile Services server script reference]: http://go.microsoft.com/fwlink/?LinkId=262293 [What are Notification Hubs?]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-overview/ [Send broadcast notifications to subscribers]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-aspnet-backend-android-xplat-segmented-gcm-push-notification/ [Send template-based notifications to subscribers]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-aspnet-backend-android-xplat-segmented-gcm-push-notification/ ================================================ FILE: docs/mobile-services-javascript-backend-define-custom-api.md ================================================ # How to: define a custom API endpoint in a JavaScript backend mobile service > [AZURE.SELECTOR] - [JavaScript backend](./mobile-services-javascript-backend-define-custom-api.md) - [.NET backend](./mobile-services-dotnet-backend-define-custom-api.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to define a custom API endpoint in a JavaScript backend mobile service. A custom API lets you define custom endpoints with server functionality, but it does not map to a database insert, update, delete, or read operation. By using a custom API, you have more control over messaging, including HTTP headers and body format. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then select your mobile service. 2. Click the **API** tab, and then click **Create**. This displays the **Create a new custom API** dialog. 3. Type _completeall_ in **API name**, and then click the check button to create the new API. > [AZURE.TIP] With default permissions, anyone with the app key may call the custom API. However, the application key is not considered a secure credential because it may not be distributed or stored securely. Consider restricting access to only authenticated users for additional security. 4. Click on **completeall** in the API table. 5. Click the **Script** tab, replace the existing code with the following code, then click **Save**. This code uses the [mssql object] to access the **todoitem** table directly to set the `complete` flag on all items. Because the **exports.post** function is used, clients send a POST request to perform the operation. The number of changed rows is returned to the client as an integer value. exports.post = function(request, response) { var mssql = request.service.mssql; var sql = "UPDATE todoitem SET complete = 1 " + "WHERE complete = 0; SELECT @@ROWCOUNT as count"; mssql.query(sql, { success: function(results) { if(results.length == 1) response.send(200, results[0]); } }) }; > [AZURE.NOTE] The [request](http://msdn.microsoft.com/library/windowsazure/jj554218.aspx) and [response](http://msdn.microsoft.com/library/windowsazure/dn303373.aspx) object supplied to custom API functions are implemented by the [Express.js library](http://go.microsoft.com/fwlink/p/?LinkId=309046). [mssql object]: http://msdn.microsoft.com/library/windowsazure/jj554212.aspx For information on how to invoke a custom API in your app using a Mobile Services client library, see [Call a custom API](mobile-services-windows-dotnet-how-to-use-client-library.md#custom-api) in the client SDK reference. ================================================ FILE: docs/mobile-services-javascript-backend-ios-get-started-push.md ================================================ # Add Push Notifications to iOS App and JavaScript Backend > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to add push notifications to the [quickstart project](mobile-services-ios-get-started.md), so that your mobile service sends a push notification each time a record is inserted. You must complete [Get Started with Mobile Services] first. > [AZURE.NOTE] The [iOS simulator does not support push notifications](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/TestingontheiOSSimulator.html), so you must use a physical iOS device. You'll also need to sign up for a paid [Apple Developer Program membership](https://developer.apple.com/programs/ios/). * [Register an App ID for your app](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingProfiles/MaintainingProfiles.html#//apple_ref/doc/uid/TP40012582-CH30-SW991). Create an explicit App ID (not a wildcard App ID) and for **Bundle ID**, use the exact **Bundle ID** that is in your Xcode quickstart project. It is also crucial that you check the **Push Notifications** option. * Next, [configuring push notifications](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html#//apple_ref/doc/uid/TP40012582-CH26-SW6). You may create either a "Development" or "Distribution" SSL certificate (remember to select the corresponding option in the Azure portal later.) ## Configure Azure to Send Push Notifications * Follow the steps at [Installing a Client SSL Signing Identity on the Server](https://developer.apple.com/library/prerelease/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html#//apple_ref/doc/uid/TP40012582-CH26-SW16) to export the certificate you downloaded in the previous step to a .p12 file. * In the Azure classic portal, click **Mobile Services** > your app > the **Push** tab > **apple push notification settings** > "**Upload**. Upload the .p12 file, making sure that the correct **Mode** is selected (either Sandbox or Production, corresponding to whether the client SSL certificate you generated was Development or Distribution.) Your mobile service is now configured to work with push notifications on iOS! ## Update Backend Script to Send Push Notifications * In the [Azure classic portal], click the **Data** tab and then click **TodoItem**. In **TodoItem**, click the **Script** tab and select **Insert**. This displays the function that is invoked when an insert occurs in the **TodoItem** table. * Replace the insert function with the following code, and then click **Save**. This registers a new insert script, which uses the [apns object] to send a push notification (the inserted text) to the device provided in the insert request. This script delays sending the notification to give you time to close the app to receive a push notification. ``` function insert(item, user, request) { request.execute(); // Set timeout to delay the notification, to provide time for the // app to be closed on the device to demonstrate push notifications setTimeout(function() { push.apns.send(null, { alert: "Alert: " + item.text, payload: { inAppMessage: "Hey, a new item arrived: '" + item.text + "'" } }); }, 2500); } ``` ## Add Push Notifications to App * In QSAppDelegate.m, import the iOS SDK and QSTodoService.h: ``` #import #import "QSTodoService.h" ``` * In `didFinishLaunchingWithOptions` in QSAppDelegate.m, insert the following lines right before `return YES;`: ``` UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil]; [[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings]; [[UIApplication sharedApplication] registerForRemoteNotifications]; ``` * In QSAppDelegate.m, add the following handler methods. Your app is now updated to support push notifications. ``` // Registration with APNs is successful - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { QSTodoService *todoService = [QSTodoService defaultService]; MSClient *client = todoService.client; [client.push registerNativeWithDeviceToken:deviceToken tags:nil completion:^(NSError *error) { if (error != nil) { NSLog(@"Error registering for notifications: %@", error); } }]; } // Handle any failure to register - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError: (NSError *)error { NSLog(@"Failed to register for remote notifications: %@", error); } // Use userInfo in the payload to display a UIAlertView. - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { NSLog(@"%@", userInfo); NSDictionary *apsPayload = userInfo[@"aps"]; NSString *alertString = apsPayload[@"alert"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notification" message:alertString delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } ``` * In Xcode, press **Run** and start the app on an iOS device (not the simulator.) Click **OK** to accept push notifications; this request occurs the first time the app runs. * In the app, add a new item and click **+**. * Verify that a notification is received, then click **OK** to dismiss the notification. You have now successfully completed this tutorial. ![](media/mobile-services-ios-get-started-push/mobile-quickstart-push3-ios.png) [5]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step5.png [6]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step6.png [7]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step7.png [9]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step9.png [10]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step10.png [17]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step17.png [18]: ./media/mobile-services-ios-get-started-push/mobile-services-selection.png [19]: ./media/mobile-services-ios-get-started-push/mobile-push-tab-ios.png [20]: ./media/mobile-services-ios-get-started-push/mobile-push-tab-ios-upload.png [21]: ./media/mobile-services-ios-get-started-push/mobile-portal-data-tables.png [22]: ./media/mobile-services-ios-get-started-push/mobile-insert-script-push2.png [23]: ./media/mobile-services-ios-get-started-push/mobile-quickstart-push1-ios.png [24]: ./media/mobile-services-ios-get-started-push/mobile-quickstart-push2-ios.png [25]: ./media/mobile-services-ios-get-started-push/mobile-quickstart-push3-ios.png [26]: ./media/mobile-services-ios-get-started-push/mobile-quickstart-push4-ios.png [28]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-step18.png [101]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-01.png [102]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-02.png [103]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-03.png [104]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-04.png [105]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-05.png [106]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-06.png [107]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-07.png [108]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-08.png [110]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-10.png [111]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-11.png [112]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-12.png [113]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-13.png [114]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-14.png [115]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-15.png [116]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-16.png [117]: ./media/mobile-services-ios-get-started-push/mobile-services-ios-push-17.png [Install Xcode]: https://go.microsoft.com/fwLink/p/?LinkID=266532 [iOS Provisioning Portal]: http://go.microsoft.com/fwlink/p/?LinkId=272456 [Mobile Services iOS SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Apple Push Notification Service]: http://go.microsoft.com/fwlink/p/?LinkId=272584 [Get started with Mobile Services]: mobile-services-ios-get-started.md [Get started with authentication]: mobile-services-ios-get-started-users.md [Azure classic portal]: https://manage.windowsazure.com/ [apns object]: http://go.microsoft.com/fwlink/p/?LinkId=272333 [Mobile Services server script reference]: http://go.microsoft.com/fwlink/?LinkId=262293 [Send push notifications to authenticated users]: mobile-services-javascript-backend-ios-push-notifications-app-users.md [What are Notification Hubs?]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-overview/ [Send broadcast notifications to subscribers]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-ios-xplat-segmented-apns-push-notification/ [Send template-based notifications to subscribers]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-ios-xplat-localized-apns-push-notification/ [Mobile Services Objective-C how-to conceptual reference]: mobile-services-windows-dotnet-how-to-use-client-library.md ================================================ FILE: docs/mobile-services-javascript-backend-ios-push-notifications-app-users.md ================================================ # Send Push Notifications to Authenticated Users > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-push-notifications-app-users.md) - [(Windows 8.x Store C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-push-notifications-app-users.md) - [(Windows 8.x Store C# | JavaScript)](mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). In this topic, you learn how to send push notifications to an authenticated user on iOS. Before starting this tutorial, complete [Get started with authentication] and [Get started with push notifications] first. In this tutorial, you require users to authenticate first, register with the notification hub for push notifications, and update server scripts to send those notifications to only authenticated users. ## Update Service to Require Authentication to Register 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your mobile service. 2. Click the **Push** tab, select **Only Authenticated Users** for **Permissions**, click **Save**, and then click **Edit Script**. This allows you to customize the push notification registration callback function. If you use Git to edit your source code, this same registration function is found in `.\service\extensions\push.js`. 3. Replace the existing **register** function with the following code and then click **Save**: exports.register = function (registration, registrationContext, done) { // Get the ID of the logged-in user. var userId = registrationContext.user.userId; // Perform a check here for any disallowed tags. if (!validateTags(registration)) { // Return a service error when the client tries // to set a user ID tag, which is not allowed. done("You cannot supply a tag that is a user ID"); } else{ // Add a new tag that is the user ID. registration.tags.push(userId); // Complete the callback as normal. done(); } }; function validateTags(registration){ for(var i = 0; i < registration.tags.length; i++) { console.log(registration.tags[i]); if (registration.tags[i] .search(/facebook:|twitter:|google:|microsoft:/i) !== -1){ return false; } return true; } } This adds a tag to the registration that is the ID of the logged-in user. The supplied tags are validated to prevent a user from registering for another user's ID. When a notification is sent to this user, it is received on this and any other device registered by the user. 4. Click the back arrow, click the **Data** tab, click **TodoItem**, click **Script**, and then select **Insert**. Replace the `insert` function with the following code, then click **Save**. This insert script uses the user ID tag to send a push notification to all iOS app registrations from the logged-in user: ``` // Get the ID of the logged-in user. var userId = user.userId; function insert(item, user, request) { request.execute(); setTimeout(function() { push.apns.send(userId, { alert: "Alert: " + item.text, payload: { "Hey, a new item arrived: '" + item.text + "'" } }); }, 2500); } ``` ## Update App to Login Before Registration Next, you need to change the way that push notifications are registered so that a user is authenticated before registration is attempted. 1. In **QSAppDelegate.m**, remove the implementation of **didFinishLaunchingWithOptions** altogether. 2. Open **QSTodoListViewController.m** and add the following code to the end of the **viewDidLoad** method: ``` // Register for remote notifications [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound]; ``` ## Test App 1. Press **Run** to start the app on a physical iOS device. In the app, add a new item, such as _A new Mobile Services task_, to the todo list. 2. Verify that a notification is received. Additionally -- and optionally -- repeat the above steps on a different physical iOS device, once using the same log-in account and another time using a different log-in account. Verify that notifications are received only by devices authenticating with the same user account. [Updating the service to require authentication for registration]: #register [Updating the app to log in before registration]: #update-app [Testing the app]: #test [Next Steps]:#next-steps [Get started with authentication]: mobile-services-ios-get-started-users.md [Get started with push notifications]: mobile-services-javascript-backend-ios-get-started-push.md [Mobile Services .NET How-to Conceptual Reference]: mobile-services-ios-how-to-use-client-library.md ================================================ FILE: docs/mobile-services-javascript-backend-phonegap-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.TIP] This topic shows you how to get started with Mobile Services as quickly as possible. It is designed for customers new to this Azure feature. If you are already familiar with Mobile Services or are looking for more in-depth information, please select a topic from the left-navigation or see the relevant links in [Next steps](#next-steps). This tutorial shows you how to add a cloud-based backend service to an app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple _To do list_ app that stores app data in the new mobile service. A screenshot from the completed app is below: ![][3] ### Additional Requirements Completing this tutorial requires the following: + PhoneGap tools (v3.2+ required for Windows Phone 8 projects). + An active Microsoft Azure account. + PhoneGap supports developing for multiple platforms. In addition to the PhoneGap tools themselves, you must install the tools for each platform you are targeting: - Windows Phone: Install [Visual Studio 2012 Express for Windows Phone](https://go.microsoft.com/fwLink/p/?LinkID=268374) - iOS: Install [Xcode] (v4.4+ required) - Android: Install the [Android Developer Tools][Android SDK]
(The Mobile Services SDK for Android supports apps for Android 2.2 or a later version. Android 4.2 or higher is required to run the quick start app.) ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new PhoneGap app In this section you will create a new PhoneGap app that is connected to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **PhoneGap** under **Choose platform** and expand **Create a new PhoneGap app**. ![][0] This displays the three easy steps to create a PhoneGap app connected to your mobile service. ![][1] 3. If you haven't already done so, download and install PhoneGap and at least one of the platform development tools (Windows Phone, iOS, or Android). 4. Click **Create TodoItem table** to create a table to store app data. 5. Under **Download and run your app**, click **Download**. This downloads the project for the sample _To do list_ application that is connected to your mobile service, along with the Mobile Services JavaScript SDK. Save the compressed project file to your local computer, and make a note of where you saved it. ## Run your new PhoneGap app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files and expand the files on your computer. 2. Open and run the project according to the instructions below for each platform. + **Windows Phone 8** 1. Windows Phone 8: Open the .sln file in the **platforms\wp8** folder in Visual Studio 2012 Express for Windows Phone. 2. Press the **F5** key to rebuild the project and start the app. ![][2] + **iOS** 1. Open the project in the **platforms/ios** folder in Xcode. 2. Press the **Run** button to build the project and start the app in the iPhone emulator, which is the default for this project. ![][3] + **Android** 1. In Eclipse, click **File** then **Import**, expand **Android**, click **Existing Android Code into Workspace**, and then click **Next.** 2. Click **Browse**, browse to the location of the expanded project files, click **OK**, make sure that the TodoActivity project is checked, then click **Finish**.

This imports the project files into the current workspace.

3. From the **Run** menu, click **Run** to start the project in the Android emulator. ![][4] >[AZURE.NOTE]To be able to run the project in the Android emulator, you must define a least one Android Virtual Device (AVD). Use the AVD Manager to create and manage these devices. 3. After launching the app in one of the mobile emulators above, type some text into the textbox and then click **Add**. This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the **TodoItem** table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. > [AZURE.IMPORTANT] Changes to this platform project will be overwritten if the main project is rebuilt with the PhoneGap tools. Instead, make changes in the project's root www directory as outlined in the section below. 4. Back in the [Azure classic portal], click the **Data** tab and then click the **TodoItem** table. ![](./media/mobile-services-javascript-backend-phonegap-get-started/mobile-data-tab.png) This lets you browse the data inserted by the app into the table. ![](./media/mobile-services-javascript-backend-phonegap-get-started/mobile-data-browse.png) ## Make app updates and rebuild projects for each platform 1. Make changes to code files in the ´www´ directory, which in this case is ´todolist/www´. 2. Verify that all of the target platform tools are accessible in the system path. 2. Open a command prompt in the root project directory, and run one of the following platform-specific commands: + **Windows Phone** Run the following command from the Visual Studio Developer command prompt: phonegap local build wp8 + **iOS** Open terminal and run the following command: phonegap local build ios + **Android** Open a command prompt or terminal window and run the following command. phonegap local build android 4. Open each project in the appropriate development environment as outlined in the previous section. >[AZURE.NOTE]You can review the code that accesses your mobile service to query and insert data, which is found in the js/index.js file. ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * **[Add authentication to your app]** Learn how to authenticate users of your app with an identity provider. * **[Add push notifications to your app](https://msdn.microsoft.com/magazine/dn879353.aspx)** Learn how to register for and send push notifications to your app. * **[Mobile Services HTML/JavaScript How-to Conceptual Reference](mobile-services-html-how-to-use-client-library.md)** Learn more about how to use the JavaScript client library to access data, call custom APIs, and perform authentication. [0]: ./media/mobile-services-javascript-backend-phonegap-get-started/portal-screenshot1.png [1]: ./media/mobile-services-javascript-backend-phonegap-get-started/portal-screenshot2.png [2]: ./media/mobile-services-javascript-backend-phonegap-get-started/mobile-portal-quickstart-wp8.png [3]: ./media/mobile-services-javascript-backend-phonegap-get-started/mobile-portal-quickstart-ios.png [4]: ./media/mobile-services-javascript-backend-phonegap-get-started/mobile-portal-quickstart-android.png [Add authentication to your app]: mobile-services-html-get-started-users.md [Android SDK]: https://go.microsoft.com/fwLink/p/?LinkID=280125 [Azure classic portal]: https://manage.windowsazure.com/ [Xcode]: https://go.microsoft.com/fwLink/p/?LinkID=266532 [Visual Studio 2012 Express for Windows Phone]: https://go.microsoft.com/fwLink/p/?LinkID=268374 ================================================ FILE: docs/mobile-services-javascript-backend-service-side-authorization.md ================================================ # Service-side authorization of users in Mobile Services > [AZURE.SELECTOR] - [.NET backend](mobile-services-dotnet-backend-service-side-authorization.md) - [Javascript backend](mobile-services-javascript-backend-service-side-authorization.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). > For the equivalent Mobile Apps version of this topic, see [this sample code](https://github.com/Azure/azure-mobile-apps-node/blob/master/samples/personal-table/tables/TodoItem.js#L38). This topic shows you how to use server-side scripts to authorize users. In this tutorial, you register scripts with Azure Mobile Services, filter queries based on user IDs, and give users access to only their own data. Filtering a user's query results by the user ID is the most basic form of authorization. Depending on your specific scenario, you might also want to create Users or Roles tables to track more detailed user authorization information, such as which endpoints a given user is permitted to access. This tutorial is based on the Mobile Services Quick Start and builds on the [Add Authentication to Existing Mobile Services App] tutorial. Please complete [Add Authentication to Existing Mobile Services App] first. ## Register scripts 1. Log on to the [Azure classic portal], click **Mobile Services**, and then click on your mobile service. Click the **Data** tab, then click the **TodoItem** table. 2. Click **Script**, select the **Insert** operation, replace the existing script with the following function, and then click **Save**. function insert(item, user, request) { item.userId = user.userId; request.execute(); } This script adds the user ID of the authenticated user to the item before insertion. >[AZURE.NOTE] Make sure that [dynamic schema](https://msdn.microsoft.com/library/azure/jj193175.aspx) is enabled. Otherwise, the *userId* column is not added automatically. This setting is enabled by default for a new mobile service. 3. Similarly, replace the existing **Read** operation with the following function. This script filters returned TodoItem objects so that a user receives only the items that they insert themselves. function read(query, user, request) { query.where({ userId: user.userId }); request.execute(); } ## Test the app 1. Notice that when you now run your client-side app, although there are items already in the _TodoItem_ table from previous tutorials, no items are returned. This happens because previous items were inserted without the user ID column and now have null values. Verify newly added items have an associated userId value in the _TodoItem_ table. 2. If you have additional login accounts, verify that users can only see their own data by closing and deleting the app and running it again. When the login credentials dialog is displayed, enter a different login and verify that items entered under the previous login are not displayed. [Register server scripts]: #register-scripts [Next Steps]:#next-steps [Windows Push Notifications & Live Connect]: http://go.microsoft.com/fwlink/p/?LinkID=257677 [Mobile Services server script reference]: http://go.microsoft.com/fwlink/p/?LinkId=262293 [My Apps dashboard]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Add Authentication to Existing Mobile Services App]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-ios [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-javascript-backend-windows-phone-get-started-push.md ================================================ # Add push notifications to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md) ## Overview This topic shows you how to use Azure Mobile Services to send push notifications to a Windows Phone Silverlight app. In this tutorial you enable push notifications using Azure Notification Hubs to the quickstart project. When complete, your mobile service will send a push notification using Notification Hubs each time a record is inserted. The notification hub that you create is free with your mobile service, can be managed independent of the mobile service, and can be used by other applications and services. This tutorial is based on the TodoList sample app. Before you start this tutorial, you must first complete the topic [Add Mobile Services to an existing app] to connect your project to the mobile service. When a mobile service has not been connected, the Add Push Notification wizard can create this connection for you. >[AZURE.NOTE] To send push notifications to a Windows Phone 8.1 Store app, follow the [Windows Store app](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) version of this tutorial. ## Update the app to register for notifications Before your app can receive push notifications, you must register a notification channel. 1. In Visual Studio, open the file App.xaml.cs and add the following `using` statement: using Microsoft.Phone.Notification; 3. Add the following to App.xaml.cs: public static HttpNotificationChannel CurrentChannel { get; private set; } private void AcquirePushChannel() { CurrentChannel = HttpNotificationChannel.Find("MyPushChannel"); if (CurrentChannel == null) { CurrentChannel = new HttpNotificationChannel("MyPushChannel"); CurrentChannel.Open(); CurrentChannel.BindToShellToast(); } CurrentChannel.ChannelUriUpdated += new EventHandler(async (o, args) => { // Register for notifications using the new channel await MobileService.GetPush() .RegisterNativeAsync(CurrentChannel.ChannelUri.ToString()); }); } This code retrieves the ChannelURI for the app from the Microsoft Push Notification Service (MPNS) used by Windows Phone 8.x "Silverlight", and then registers that ChannelURI for push notifications. >[AZURE.NOTE]In this this tutorial, the mobile service sends a toast notification to the device. When you send a tile notification, you must instead call the **BindToShellTile** method on the channel. 4. At the top of the **Application_Launching** event handler in App.xaml.cs, add the following call to the new **AcquirePushChannel** method: AcquirePushChannel(); This makes sure that registration is requested every time that the page is loaded. In your app, you may only want to make this registration periodically to ensure that the registration is current. 5. Press the **F5** key to run the app. A popup dialog with the registration key is displayed. 6. In the Solution Explorer, expand **Properties**, open the WMAppManifest.xml file, click the **Capabilities** tab and make sure that the **ID___CAP___PUSH_NOTIFICATION** capability is checked. ![Enable notifications in VS](./media/mobile-services-javascript-backend-windows-phone-get-started-push/mobile-app-enable-push-wp8.png) This makes sure that your app can raise toast notifications. ## Update server scripts to send push notifications Finally, you must update the script registered to the insert operation on the TodoItem table to send notifications. 1. Click **TodoItem**, click **Script** and select **Insert**. 2. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Define a payload for the Windows Phone toast notification. var payload = '' + '' + 'New Item' + item.text + ''; request.execute({ success: function() { // If the insert succeeds, send a notification. push.mpns.send(null, payload, 'toast', 22, { success: function(pushResponse) { console.log("Sent push:", pushResponse); request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); request.respond(500, { error: pushResponse }); } }); } }); } This insert script sends a push notification (with the text of the inserted item) to all Windows Phone app registrations after the insert succeeds. 3. Click the **Push** tab, check **Enable unauthenticated push notifications**, then click **Save**. This enables the mobile service to connect to MPNS in unauthenticated mode to send push notifications. >[AZURE.NOTE]This tutorial uses MPNS in unauthenticated mode. In this mode, MPNS limits the number of notifications that can be sent to a device channel. To remove this restriction, you must generate and upload a certificate by clicking **Upload** and selecting the certificate. For more information on generating the certificate, see [Setting up an authenticated web service to send push notifications for Windows Phone]. ## Test push notifications in your app 1. In Visual Studio, press the F5 key to run the app. >[AZURE.NOTE] You may encounter a 401 Unauthorized RegistrationAuthorizationException when testing on the Windows Phone emulator. This can occur during the `RegisterNativeAsync()` call because of the way the Windows Phone emulator syncs it's clock with the host PC. It can result in a security token that will be rejected. To resolve this simply manually set the clock in the emulator before testing. 5. In the app, enter the text "hello push" in the textbox, click **Save**, then immediately click the start button or back button to leave the app. ![Enter text into the app](./media/mobile-services-javascript-backend-windows-phone-get-started-push/mobile-quickstart-push3-wp8.png) This sends an insert request to the mobile service to store the added item. Notice that the device receives a toast notification that says **hello push**. ![Toast notification received](./media/mobile-services-javascript-backend-windows-phone-get-started-push/mobile-quickstart-push5-wp8.png) >[AZURE.NOTE]You will not receive the notification when you are still in the app. To receive a toast notification while the app is active, you must handle the [ShellToastNotificationReceived](http://msdn.microsoft.com/library/windowsphone/develop/microsoft.phone.notification.httpnotificationchannel.shelltoastnotificationreceived.aspx) event. ## Next steps This tutorial demonstrated the basics of enabling a Windows Store app to use Mobile Services and Notification Hubs to send push notifications. Next, consider completing one of the following tutorials: + [Send broadcast notifications to subscribers](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-windows-phone-push-xplat-segmented-mpns-notification/)
Learn how users can register and receive push notifications for categories they're interested in. + [Send platform-agnostic notifications to subscribers](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-aspnet-cross-platform-notification/)
Learn how to use templates to send push notifications from your mobile service, without having to craft platform-specific payloads in your back-end. Learn more about Mobile Services and Notification Hubs in the following topics: * [Azure Notification Hubs - Diagnosis guidelines](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-fixer/)
Learn how to troubleshoot your push notification issues. * [Get started with authentication]
Learn how to authenticate users of your app with different account types using mobile services. * [What are Notification Hubs?]
Learn more about how Notification Hubs works to deliver notifications to your apps across all major client platforms. * [Mobile Services .NET How-to Conceptual Reference]
Learn more about how to use Mobile Services with .NET. * [Mobile Services server script reference]
Learn more about how to implement business logic in your mobile service. [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Add Mobile Services to an existing app]: mobile-services-windows-phone-get-started-data.md [Get started with authentication]: mobile-services-windows-phone-get-started-users.md [Setting up an authenticated web service to send push notifications for Windows Phone]: http://msdn.microsoft.com/library/windowsphone/develop/ff941099(v=vs.105).aspx [Mobile Services server script reference]: http://go.microsoft.com/fwlink/?LinkId=262293 [Mobile Services .NET How-to Conceptual Reference]: mobile-services-windows-dotnet-how-to-use-client-library.md [What are Notification Hubs?]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-overview/ ================================================ FILE: docs/mobile-services-javascript-backend-windows-store-dotnet-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a universal Windows app using Azure Mobile Services. Universal Windows app solutions include projects for both Windows Store 8.1 and Windows Phone Store 8.1 apps and a common shared project. For more information, see [Build universal Windows apps that target Windows and Windows Phone](http://msdn.microsoft.com/library/windows/apps/xaml/dn609832.aspx). In this tutorial, you will create both a new mobile service and a simple *To do list* app that stores app data in the new mobile service. The mobile service that you will create uses JavaScript for server-side business logic. To create a mobile service that lets you write your server-side business logic in the supported .NET languages using Visual Studio, see the .NET backend version of this topic. The following are screen captures from the completed app: ![](./media/mobile-services-windows-universal-get-started/mobile-quickstart-completed.png)
Windows Store app ![](./media/mobile-services-windows-universal-get-started/mobile-quickstart-completed-wp8.png)
Windows Phone Store app Completing this tutorial is a prerequisite for all other Mobile Services tutorials for Windows Store and Windows Phone Store apps. To complete this tutorial, you need the following: * An active Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A0E0E5C02&returnurl=http%3A%2F%2Fazure.microsoft.com%2Fen-us%2Fdocumentation%2Farticles%2Fmobile-services-javascript-backend-windows-store-javascript-get-started%2F). * [Visual Studio 2013 Express for Windows] ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new universal Windows app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new universal Windows app or modify an existing Windows Store or Windows Phone app project to connect to your mobile service. In this section you will create a new universal Windows app that is connected to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Windows** under **Choose platform** and expand **Create a new Windows Store app**. This displays the three easy steps to create a Windows Store app connected to your mobile service. ![Mobile Services quickstart steps](./media/mobile-services-javascript-backend-windows-store-dotnet-get-started/mobile-quickstart-steps.png) 3. If you haven't already done so, download and install [Visual Studio 2013 Express for Windows] on your local computer or virtual machine. 4. Click **Create TodoItem table** to create a table to store app data. 5. Under **Download and run your app**, select a language for your app, then click **Download**. This downloads the project for the sample *To do list* application that is connected to your mobile service. Save the compressed project file to your local computer, and make a note of where you save it. ## Run your Windows app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files, expand the files on your computer, and open the solution file in Visual Studio. 2. Press the **F5** key to rebuild the project and start the app. 3. In the app, type meaningful text, such as *Complete the tutorial*, in **Insert a TodoItem**, and then click **Save**. This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the second column in the app. 4. (Optional) In a universal Windows solution, change the default start up project to the other app and run the app again. Notice that data saved from the previous step is loaded from the mobile service after the app starts. 4. Back in the [Azure classic portal](https://manage.windowsazure.com/), click the **Data** tab and then click the **TodoItems** table. This lets you browse the data inserted by the app into the table. ![](./media/mobile-services-javascript-backend-run-app/mobile-data-browse.png) >[AZURE.NOTE]You can review the code that accesses your mobile service to query and insert data, which is found in the MainPage.xaml.cs file. ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync] Learn how to use offline data sync to make your app responsive and robust. * [Add authentication to your Mobile Services app ][Get started with authentication] Learn how to authenticate users of your app with an identity provider. * [Add push notifications to your app][Get started with push notifications] Learn how to send a very basic push notification to your app. * [How to use the .NET client library](mobile-services-dotnet-how-to-use-client-library.md) Learn how to query the mobile service, work with data, and access custom APIs. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [Get started with offline data sync]: mobile-services-windows-store-dotnet-get-started-offline-data.md [Get started with authentication]: mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md [Get started with push notifications]: mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md [Visual Studio 2013 Express for Windows]: http://go.microsoft.com/fwlink/?LinkId=257546 [Mobile Services SDK]: http://go.microsoft.com/fwlink/?LinkId=257545 [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md ================================================ # Send push notifications to authenticated users > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-push-notifications-app-users.md) - [(Windows 8.x Store C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-push-notifications-app-users.md) - [(Windows 8.x Store C# | JavaScript)](mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Overview This topic shows you how to send push notifications to an authenticated user on any registered device. Unlike the previous [Add push notifications to your app] tutorial, this tutorial changes your mobile service to require that a user be authenticated before the client can register with the notification hub for push notifications. Registration is also modified to add a tag based on the assigned user ID. Finally, the server script is updated to send the notification only to the authenticated user instead of to all registrations. This tutorial walks you through the following process: 1. [Updating the service to require authentication for registration] 2. [Updating the app to log in before registration] 3. [Testing the app] This tutorial supports both Windows Store and Windows Phone Store apps. ## Prerequisites Before you start this tutorial, you must have already completed these Mobile Services tutorials: + [Add authentication to your app]
Adds a login requirement to the TodoList sample app. + [Add push notifications to your app]
Configures the TodoList sample app for push notifications by using Notification Hubs. After you have completed both tutorials, you can prevent unauthenticated users from registering for push notifications from your mobile service. ## Update the service to require authentication to register 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your mobile service. 2. Click the **Push** tab, select **Only Authenticated Users** for **Permissions**, click **Save**, and then click **Edit Script**. This allows you to customize the push notification registration callback function. If you use Git to edit your source code, this same registration function is found in `.\service\extensions\push.js`. 3. Replace the existing **register** function with the following code and then click **Save**: exports.register = function (registration, registrationContext, done) { // Get the ID of the logged-in user. var userId = registrationContext.user.userId; // Perform a check here for any disallowed tags. if (!validateTags(registration)) { // Return a service error when the client tries // to set a user ID tag, which is not allowed. done("You cannot supply a tag that is a user ID"); } else{ // Add a new tag that is the user ID. registration.tags.push(userId); // Complete the callback as normal. done(); } }; function validateTags(registration){ for(var i = 0; i < registration.tags.length; i++) { console.log(registration.tags[i]); if (registration.tags[i] .search(/facebook:|twitter:|google:|microsoft:/i) !== -1){ return false; } return true; } } This adds a tag to the registration that is the ID of the logged-in user. The supplied tags are validated to prevent a user from registering for another user's ID. When a notification is sent to this user, it is received on this and any other device registered by the user. 4. Click the back arrow, click the **Data** tab, click **TodoItem**, click **Script**, and then select **Insert**.   5. Replace the insert function with the following code, then click **Save**: function insert(item, user, request) { // Define a payload for the Windows Store toast notification. var payload = '' + '' + item.text + ''; // Get the ID of the logged-in user. var userId = user.userId; request.execute({ success: function() { // If the insert succeeds, send a notification to all devices // registered to the logged-in user as a tag. push.wns.send(userId, payload, 'wns/toast', { success: function(pushResponse) { console.log("Sent push:", pushResponse); request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); request.respond(500, { error: pushResponse }); } }); } }); }   This insert script uses the user ID tag to send a push notification (with the text of the inserted item) to all Windows Store app registrations created by the logged-in user. ## Update the app to log in before registration Next, you need to change the way that push notifications are registered to make sure that the user is authenticated before registration is attempted. The client app updates depend on the way in which you implemented push notifications. ### Using the Add Push Notification Wizard in Visual Studio 2013 Update 2 or a later version In this method, the wizard generated a new push.register.cs file in your project. 1. In Visual Studio in Solution Explorer, open the app.xaml.cs project file and in the **OnLaunched** event handler comment-out or delete the call to the **UploadChannel** method. 2. Open the push.register.cs project file and replace the **UploadChannel** method, with the following code: public async static void UploadChannel() { var channel = await Windows.Networking.PushNotifications.PushNotificationChannelManager .CreatePushNotificationChannelForApplicationAsync(); try { // Create a native push notification registration. await App.MobileService.GetPush().RegisterNativeAsync(channel.Uri); } catch (Exception exception) { HandleRegisterException(exception); } } This makes sure that registration is done using the same client instance that has the authenticated user credentials. Otherwise, registration will fail with an Unauthorized (401) error. 3. Open the shared MainPage.cs project file, and replace the **ButtonLogin_Click** handler with the following: private async void ButtonLogin_Click(object sender, RoutedEventArgs e) { // Login the user and then load data from the mobile service. await AuthenticateAsync(); todolistPush.UploadChannel(); // Hide the login button and load items from the mobile service. this.ButtonLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed; await RefreshTodoItems(); } This makes sure that authentication occurs before push registration is attempted. 4. In the previous code, replace the generated push class name (`todolistPush`) with the name of class generated by the wizard, usually in the format mobile_servicePush. ### Manually enabled push notifications In this method, you added registration code from the tutorial directly to the app.xaml.cs project file. 1. In Visual Studio in Solution Explorer, open the app.xaml.cs project file and in the **OnLaunched** event handler comment-out or delete the call to **InitNotificationsAsync**. 2. Change the accessibility of the **InitNotificationsAsync** method from `private` to `public` and add the `static` modifier. 3. Open the shared MainPage.cs project file, and replace the **ButtonLogin_Click** handler with the following: private async void ButtonLogin_Click(object sender, RoutedEventArgs e) { // Login the user and then load data from the mobile service. await AuthenticateAsync(); App.InitNotificationsAsync(); // Hide the login button and load items from the mobile service. this.ButtonLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed; await RefreshTodoItems(); } This makes sure that authentication occurs before push registration is attempted. ## Test the app 1. In Visual Studio, press the F5 key to run the app. 2. Log in using the selected identity provider and verify that authentication succeeds. 3. In the app, type text in **Insert a TodoItem**, and then click **Save**. Note that after the insert completes, the app receives a push notification from WNS. 4. (Optional) Repeat steps 1-3 on a different client device and using a different account when logging in. Verify that the notification is received only on this device, since the previous device was not tagged with the current user ID. [Updating the service to require authentication for registration]: #register [Updating the app to log in before registration]: #update-app [Testing the app]: #test [Next Steps]:#next-steps [Add authentication to your app]: mobile-services-windows-store-dotnet-get-started-users.md [Add push notifications to your app]: mobile-services-javascript-backend-windows-store-dotnet-get-started-push.md ================================================ FILE: docs/mobile-services-javascript-backend-windows-store-javascript-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a Windows Store JavaScript app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple *To do list* app that stores app data in the new mobile service. The mobile service that you will create uses JavaScript for server-side business logic. To complete this tutorial, you need the following: * An active Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A0E0E5C02&returnurl=http%3A%2F%2Fazure.microsoft.com%2Fdocumentation%2Farticles%2Fmobile-services-javascript-backend-windows-store-javascript-get-started%2F). * [Visual Studio 2013 Express for Windows] ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Windows Store app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to create a new Windows Store 8.1 JavaScript app that connects to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Windows** under **Choose platform** and expand **Create a new Windows Store app**. 3. If you haven't already done so, download and install [Visual Studio 2013][Visual Studio 2013 Express for Windows] on your local computer or virtual machine. 4. Click **Create TodoItem table** to create a table to store app data. 5. Under **Download and run your app**, select a language for your app, then click **Download**. This downloads the project for the sample *To do list* application that is connected to your mobile service. Save the compressed project file to your local computer, and make a note of where you save it. ## Run your Windows app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files, expand the files on your computer, and open the solution file in Visual Studio. 2. Press the **F5** key to rebuild the project and start the app. 3. In the app, type meaningful text, such as *Complete the tutorial*, in **Insert a TodoItem**, and then click **Save**. This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the second column in the app. 4. (Optional) Run the app again, and notice that data saved from the previous step is loaded from the mobile service after the app starts. 4. Back in the [Azure classic portal], click the **Data** tab and then click the **TodoItems** table. This lets you browse the data inserted by the app into the table. >[AZURE.NOTE] You can review the code that accesses your mobile service to query and insert data, which is found in the default.js file. ## Next Steps Now that you have completed the quickstart, learn how to work with the [Mobile Services client for HTML/JavaScript](mobile-services-html-how-to-use-client-library.md). [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [Visual Studio 2013 Express for Windows]: http://go.microsoft.com/fwlink/?LinkId=257546 [Mobile Services SDK]: http://go.microsoft.com/fwlink/?LinkId=257545 [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md ================================================ # Add push notifications to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to use Azure Mobile Services with a JavaScript backend to send push notifications to a universal Windows app. In this tutorial you enable push notifications using Azure Notification Hubs in a universal Windows app project. When complete, your mobile service will send a push notification from the JavaScript backend to all registered Windows Store and Windows Phone Store apps each time a record is inserted in the TodoList table. The notification hub that you create is free with your mobile service, can be managed independent of the mobile service, and can be used by other applications and services. >[AZURE.NOTE]This topic shows you how to use the tooling in Visual Studio 2013 with Update 3 to add support for push notifications from Mobile Services to a universal Windows app. The same steps can be used to add push notifications from Mobile Services to a Windows Store or Windows Phone Store 8.1 app. To add push notifications to a Windows Phone 8 or Windows Phone Silverlight 8.1 app, see this version of [Get started with push notifications in Mobile Services](mobile-services-javascript-backend-windows-phone-get-started-push.md). This tutorial walks you through these basic steps to enable push notifications: 1. [Register your app for push notifications](#register) 2. [Update the service to send push notifications](#update-service) 3. [Test push notifications in your app](#test) To complete this tutorial, you need the following: * An active [Microsoft Store account](http://go.microsoft.com/fwlink/p/?LinkId=280045). * [Visual Studio 2013 Express for Windows](http://go.microsoft.com/fwlink/?LinkId=257546) with Update 3, or a later version ## Register your app for push notifications The following steps registers your app with the Windows Store, configure your mobile service to enable push notifications, and add code to your app to register a device channel with your notification hub. Visual Studio 2013 connects to Azure and to the Windows Store by using the credentials that you provide. 1. In Visual Studio 2013, open Solution Explorer, right-click the Windows Store app project, click **Add** then **Push Notification...**. ![Add Push Notification wizard in Visual Studio 2013](./media/mobile-services-create-new-push-vs2013/mobile-add-push-notifications-vs2013.png) This starts the Add Push Notification Wizard. 2. Click **Next**, sign in to your Windows Store account, then supply a name in **Reserve a new name** and click **Reserve**. This creates a new app registration. 3. Click the new registration in the **App Name** list, then click **Next**. 4. In the **Select a service** page, click the name of your mobile service, then click **Next** and **Finish**. The notification hub used by your mobile service is updated with the Windows Notification Services (WNS) registration. You can now use Azure Notification Hubs to send notifications from Mobile Services to your app by using WNS. >[AZURE.NOTE]This tutorial demonstrates sending notifications from a mobile service backend. You can use the same notification hub registration to send notifications from any backend service. For more information, see [Notification Hubs Overview](http://msdn.microsoft.com/library/azure/jj927170.aspx). 5. When you complete the wizard, a new **Push setup is almost complete** page is opened in Visual Studio. This page details an alternate method to configure your mobile service project to send notifications that is different from this tutorial. The code that is added to your universal Windows app solution by the Add Push Notification wizard is platform-specific. Later in this section, you will remove this redundancy by sharing the Mobile Services client code, which makes the universal app easier to maintain. [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started/ [Get started with data]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-data-dotnet/   6. Browse to the `\Services\MobileServices\your_service_name` project folder, open the generated push.register.cs code file, and inspect the **UploadChannel** method that registers the device's channel URL with the notification hub.   7. Open the shared App.xaml.cs code file and notice that a call to the new **UploadChannel** method was added in the **OnLaunched** event handler. This makes sure that registration of the device is attempted whenever the app is launched.   8. Repeat the previous steps to add push notifications to the Windows Phone Store app project, then in the shared App.xaml.cs file, remove the extra call to **Mobile Service Client**, **UploadChannel** and the remaining `#if...#endif` conditional wrapper. Both projects can now share a single call to **UploadChannel**.   Note that you can also simplify the generated code by unifying the `#if...#endif` wrapped [MobileServiceClient] definitions into a single unwrapped definition used by both versions of the app. Now that push notifications are enabled in the app, you must update the mobile service to send push notifications. ## Update the service to send push notifications The following steps update the insert script registered to the TodoItem table. You can implement similar code in any server script or anywhere else in your backend services. Finally, you must update the script registered to the insert operation on the TodoItem table to send notifications. 1. Click **TodoItem**, click **Script** and select **Insert**. 2. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Define a payload for the Windows Store toast notification. var payload = '' + ' ' + item.text + ''; request.execute({ success: function() { // If the insert succeeds, send a notification. push.wns.send(null, payload, 'wns/toast', { success: function(pushResponse) { console.log("Sent push:", pushResponse); request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); request.respond(500, { error: pushResponse }); } }); } }); } This insert script sends a push notification (with the text of the inserted item) to all Windows Store app registrations after the insert succeeds. ## Test push notifications in your app 1. In Visual Studio, right-click the Windows Store project, click **Set as StartUp Project**, then press the F5 key to run the Windows Store app. After the app starts, the device is registered for push notifications. 2. Stop the Windows Store app and repeat the previous step to run the Windows Phone Store app. At this point, both devices are registered to receive push notifications. 3. Run the Windows Store app again, and type text in **Insert a TodoItem**, and then click **Save**. ![](./media/mobile-services-javascript-backend-windows-universal-test-push/mobile-quickstart-push1.png) Note that after the insert completes, both the Windows Store and the Windows Phone apps receive a push notification from WNS. ![](./media/mobile-services-javascript-backend-windows-universal-test-push/mobile-quickstart-push2.png) The notification is displayed on Windows Phone even when the app isn't running. ![](./media/mobile-services-javascript-backend-windows-universal-test-push/mobile-quickstart-push5-wp8.png) ## Next steps This tutorial demonstrated the basics of enabling a Windows Store app to use Mobile Services and Notification Hubs to send push notifications. Next, consider completing one of the following tutorials: + [Send push notifications to authenticated users](mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md)
Learn how to use tags to send push notifications from your mobile service to only an authenticated user. + [Send broadcast notifications to subscribers](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-windows-notification-dotnet-push-xplat-segmented-wns/)
Learn how users can register and receive push notifications for categories they're interested in. + [Send platform-agnostic notifications to subscribers](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-aspnet-cross-platform-notification/)
Learn how to use templates to send push notifications from your mobile service, without having to craft platform-specific payloads in your back-end. Learn more about Mobile Services and Notification Hubs in the following topics: * [Azure Notification Hubs - Diagnosis guidelines](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-fixer/)
Learn how to troubleshoot your push notification issues. * [Get started with authentication]
Learn how to authenticate users of your app with different account types using mobile services. * [What are Notification Hubs?]
Learn more about how Notification Hubs works to deliver notifications to your apps across all major client platforms. * [How to use a .NET client for Azure Mobile Services]
Learn more about how to use Mobile Services from C# Windows apps. [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-dotnet-backend-windows-store-dotnet-get-started.md [Get started with authentication]: mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md [Send push notifications to authenticated users]: mobile-services-javascript-backend-windows-store-dotnet-push-notifications-app-users.md [What are Notification Hubs?]: https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-overview/ [How to use a .NET client for Azure Mobile Services]: mobile-services-windows-dotnet-how-to-use-client-library.md [MobileServiceClient]: http://go.microsoft.com/fwlink/p/?LinkId=302030 ================================================ FILE: docs/mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md ================================================ # Add authentication to your universal Windows 8.1 app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to authenticate users in Azure Mobile Services from your universal Windows 8.1 app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. >[AZURE.NOTE]This tutorial shows you how to authenticate users in Windows Store and Windows Phone Store 8.1 apps. For a Windows Phone 8.0 or Windows Phone Silverlight 8.1 app, see this version of [Get started with authentication in Mobile Services](mobile-services-windows-phone-get-started-users.md). ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users 1. In the Server Explorer in Visual Studio, expand the **Azure** node, **Mobile Services**, and your mobile service. 2. Right-click the **TodoItem** table, click **Edit Permissions**, set all permissions to **Only authenticated users**, and then click **Apply**. This ensures that all operations against the _TodoItem_ table require an authenticated user. 3. Right-click the client app project, click **Debug**, then **Start new instance**; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the *TodoItem* table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. >[AZURE.NOTE] When you use Visual Studio tools to connect your app to a Mobile Service, the tool generate two sets of **MobileServiceClient** definitions, one for each client platform. This is a good time to simplify the generated code by unifying the `#if...#endif` wrapped **MobileServiceClient** definitions into a single unwrapped definition used by both versions of the app. You won't need to do this if you downloaded the quickstart app from the [Azure classic portal]. ## Add authentication to the app 1. Open the shared project file MainPage.cs and add the following code snippet to the MainPage class: // Define a member variable for storing the signed-in user. private MobileServiceUser user; // Define a method that performs the authentication process // using a Facebook sign-in. private async System.Threading.Tasks.Task AuthenticateAsync() { string message; bool success = false; try { // Change 'MobileService' to the name of your MobileServiceClient instance. // Sign-in using Facebook authentication. user = await App.MobileService .LoginAsync(MobileServiceAuthenticationProvider.Facebook); message = string.Format("You are now signed in - {0}", user.UserId); success = true; } catch (InvalidOperationException) { message = "You must log in. Login Required"; } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); return success; } This code authenticates the user with a Facebook login. If you are using an identity provider other than Facebook, change the value of **MobileServiceAuthenticationProvider** above to the value for your provider. 3. Comment-out or delete the call to the **RefreshTodoItems** method in the existing **OnNavigatedTo** method override. This prevents the data from being loaded before the user is authenticated. Next, you will add a **Sign in** button to the app that triggers authentication. 4. Add the following code snippet to the MainPage class: private async void ButtonLogin_Click(object sender, RoutedEventArgs e) { // Login the user and then load data from the mobile app. if (await AuthenticateAsync()) { // Hide the login button and load items from the mobile app. ButtonLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed; //await InitLocalStoreAsync(); //offline sync support. await RefreshTodoItems(); } } 5. In the Windows Store app project, open the MainPage.xaml project file and add the following **Button** element just before the element that defines the **Save** button: 6. In the Windows Phone Store app project, add the following **Button** element in the **ContentPanel**, after the **TextBox** element: 8. Open the shared App.xaml.cs project file and add the following code: protected override void OnActivated(IActivatedEventArgs args) { // Windows Phone 8.1 requires you to handle the respose from the WebAuthenticationBroker. #if WINDOWS_PHONE_APP if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation) { // Completes the sign-in process started by LoginAsync. // Change 'MobileService' to the name of your MobileServiceClient instance. App.MobileService.LoginComplete(args as WebAuthenticationBrokerContinuationEventArgs); } #endif base.OnActivated(args); } If the **OnActivated** method already exists, just add the `#if...#endif` code block. 9. Press the F5 key to run the Windows Store app, click the **Sign in** button, and sign into the app with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query your backend and make updates to data. 10. Right-click the Windows Phone Store app project, click **Set as StartUp Project**, then repeat the previous step to verify that the Windows Phone Store app also runs correctly. Now, any user authenticated by your trusted identity providers can access the *TodoItem* table. To better secure user-specific data, you must also implement authorization. To do this you get the user ID of a given user, which can then be used to determine what level of access that user should have for a given resource. ## Store the authorization token on the client The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the mobile service every time that the app starts. Not only is this method inefficient, you can run into usage-related issues should many customers try to start your app at the same time. A better approach is to cache the authorization token returned by Mobile Services and try to use this first before using a provider-based sign-in. >[AZURE.NOTE]You can cache the token issued by Mobile Services regardless of whether you are using client-managed or service-managed authentication. This tutorial uses service-managed authentication. 1. In the MainPage.xaml.cs project file, add the following **using** statements: using System.Linq; using Windows.Security.Credentials; 2. Replace the **AuthenticateAsync** method with the following code: private async System.Threading.Tasks.Task AuthenticateAsync() { string message; bool success = false; // This sample uses the Facebook provider. var provider = MobileServiceAuthenticationProvider.Facebook; // Use the PasswordVault to securely store and access credentials. PasswordVault vault = new PasswordVault(); PasswordCredential credential = null; try { // Try to get an existing credential from the vault. credential = vault.FindAllByResource(provider.ToString()).FirstOrDefault(); } catch (Exception) { // When there is no matching resource an error occurs, which we ignore. } if (credential != null) { // Create a user from the stored credentials. user = new MobileServiceUser(credential.UserName); credential.RetrievePassword(); user.MobileServiceAuthenticationToken = credential.Password; // Set the user from the stored credentials. App.MobileService.CurrentUser = user; // Consider adding a check to determine if the token is // expired, as shown in this post: http://aka.ms/jww5vp. success = true; message = string.Format("Cached credentials for user - {0}", user.UserId); } else { try { // Login with the identity provider. user = await App.MobileService .LoginAsync(provider); // Create and store the user credentials. credential = new PasswordCredential(provider.ToString(), user.UserId, user.MobileServiceAuthenticationToken); vault.Add(credential); success = true; message = string.Format("You are now logged in - {0}", user.UserId); } catch (MobileServiceInvalidOperationException) { message = "You must log in. Login Required"; } } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); return success; } In this version of **AuthenticateAsync**, the app tries to use credentials stored in the **PasswordVault** to access the service. A regular sign-in is also performed when there is no stored credential. >[AZURE.NOTE]A cached token may be expired, and token expiration can also occur after authentication when the app is in use. To learn how to determine if a token is expired, see [Check for expired authentication tokens](http://aka.ms/jww5vp). For a solution to handling authorization errors related to expiring tokens, see the post [Caching and handling expired tokens in Azure Mobile Services managed SDK](http://blogs.msdn.com/b/carlosfigueira/archive/2014/03/13/caching-and-handling-expired-tokens-in-azure-mobile-services-managed-sdk.aspx). 3. Restart the app twice. Notice that on the first start-up, sign-in with the provider is again required. However, on the second restart the cached credentials are used and sign-in is bypassed. ## Next steps In the next tutorial, [Service-side authorization of Mobile Services users](mobile-services-javascript-backend-service-side-authorization.md), you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. ## See also + [Enhanced users feature](http://go.microsoft.com/fwlink/p/?LinkId=506605)
You can get additional user data maintained by the identity provider in your mobile service by by calling the **user.getIdentities()** function in server scripts. + [Mobile Services .NET How-to Conceptual Reference]
Learn more about how to use Mobile Services with a .NET client. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Store authentication tokens on the client]: #tokens [Next Steps]:#next-steps [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: mobile-services-javascript-backend-windows-store-dotnet-get-started.md [Get started with authentication]: mobile-services-javascript-backend-windows-store-dotnet-get-started-users.md [Get started with push notifications]: mobile-services-javascript-backend-windows-store-dotnet-get-started-push.md [Authorize users with scripts]: mobile-services-windows-store-dotnet-authorize-users-in-scripts.md [Azure classic portal]: https://manage.windowsazure.com/ [Mobile Services .NET How-to Conceptual Reference]: mobile-services-windows-dotnet-how-to-use-client-library.md [Register your Windows Store app package for Microsoft authentication]: mobile-services-how-to-register-store-app-package-microsoft-authentication.md ================================================ FILE: docs/mobile-services-javascript-backend-windows-universal-dotnet-upload-data-blob-storage.md ================================================ # Upload images to Azure Blob storage by using Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-upload-data-blob-storage.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-upload-data-blob-storage.md) - [(Android | Javascript)](mobile-services-android-upload-data-blob-storage.md) ## Overview This topic shows you how to use Azure Mobile Services to enable your app to upload and store user-generated images in Azure Storage. Mobile Services uses a SQL Database to store data. However, binary large object (BLOB) data is more efficiently stored in Azure Blob storage service. You cannot securely distribute with the client app the credentials required to securely upload data to the Blob Storage service. Instead, you must store these credentials in your mobile service and use them to generate a Shared Access Signature (SAS) that is used to upload a new image. The SAS, a credential with a short expiration—in this case 5 minutes, is returned securely by Mobile Services to the client app. The app then uses this temporary credential to upload the image. In this example, downloads from the Blob service are public. In this tutorial you add functionality to the Mobile Services quickstart app to take pictures and upload the images to Azure by using an SAS generated by Mobile Services. ## Prerequisites This tutorial requires the following: + Microsoft Visual Studio 2013 Update 3, or a later version + [Azure Storage account](https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/) + A camera or other image capture device attached to your computer. This tutorial is based on the Mobile Services quickstart. Before you start this tutorial, you must first complete [Get started with Mobile Services]. ## Update the registered insert script in the Azure classic portal A new insert script is registered that generates an SAS when a new Todo item is inserted. 0. If you haven't yet created your storage account, see [How To Create a Storage Account](https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/). 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Storage**, click the storage account, then click **Manage Keys**. 2. Make a note of the **Storage Account Name** and **Access Key**. ![](./media/mobile-services-configure-blob-storage/mobile-blob-storage-account-keys.png) 3. In your mobile service, click the **Configure** tab, scroll down to **App settings** and enter a **Name** and **Value** pair for each of the following that you obtained from the storage account, then click **Save**. + `STORAGE_ACCOUNT_NAME` + `STORAGE_ACCOUNT_ACCESS_KEY` ![](./media/mobile-services-configure-blob-storage/mobile-blob-storage-app-settings.png) The storage account access key is stored encrypted in app settings. You can access this key from any server script at runtime. For more information, see [App settings]. 4. In the Configure tab, make sure that [Dynamic schema](http://msdn.microsoft.com/library/windowsazure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7) is enabled. You need dynamic schema enabled to be able to add new columns to the TodoItem table. Dynamic schema should not be enabled in any production service. 4. Click the **Data** tab and then click the **TodoItem** table. 5. In **todoitem**, click the **Script** tab and select **Insert**, replace the insert function with the following code, then click **Save**: var azure = require('azure'); var qs = require('querystring'); var appSettings = require('mobileservice-config').appSettings; function insert(item, user, request) { // Get storage account settings from app settings. var accountName = appSettings.STORAGE_ACCOUNT_NAME; var accountKey = appSettings.STORAGE_ACCOUNT_ACCESS_KEY; var host = accountName + '.blob.core.windows.net'; if ((typeof item.containerName !== "undefined") && ( item.containerName !== null)) { // Set the BLOB store container name on the item, which must be lowercase. item.containerName = item.containerName.toLowerCase(); // If it does not already exist, create the container // with public read access for blobs. var blobService = azure.createBlobService(accountName, accountKey, host); blobService.createContainerIfNotExists(item.containerName, { publicAccessLevel: 'blob' }, function(error) { if (!error) { // Provide write access to the container for the next 5 mins. var sharedAccessPolicy = { AccessPolicy: { Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.WRITE, Expiry: new Date(new Date().getTime() + 5 * 60 * 1000) } }; // Generate the upload URL with SAS for the new image. var sasQueryUrl = blobService.generateSharedAccessSignature(item.containerName, item.resourceName, sharedAccessPolicy); // Set the query string. item.sasQueryString = qs.stringify(sasQueryUrl.queryString); // Set the full path on the new new item, // which is used for data binding on the client. item.imageUri = sasQueryUrl.baseUrl + sasQueryUrl.path; } else { console.error(error); } request.execute(); }); } else { request.execute(); } } This replaces the function that is invoked when an insert occurs in the TodoItem table with a new script. This new script generates a new SAS for the insert, which is valid for 5 minutes, and assigns the value of the generated SAS to the `sasQueryString` property of the returned item. The `imageUri` property is also set to the resource path of the new BLOB to enable image display during binding in the client UI. >[AZURE.NOTE] This code creates an SAS for an individual BLOB. If you need to upload multiple blobs to a container using the same SAS, you can instead call the [generateSharedAccessSignature method](http://go.microsoft.com/fwlink/?LinkId=390455) with an empty blob resource name, like this: > > blobService.generateSharedAccessSignature(containerName, '', sharedAccessPolicy); Next, you will update the quickstart app to add image upload functionality by using the SAS generated on insert. [App settings]: http://msdn.microsoft.com/library/windowsazure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7 ## Install the Storage client for Windows Store apps To be able to use an SAS to upload images to Blob storage, you must first add the NuGet package that installs Storage client library for Windows Store apps. 1. In **Solution Explorer** in Visual Studio, right-click the project name, and then select **Manage NuGet Packages**. 2. In the left pane, select the **Online** category, search for `WindowsAzure.Storage`, click **Install** on the **Azure Storage** package, then accept the license agreements. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-add-storage-nuget-package-dotnet.png) This adds the client library for Azure storage services to the project. Next, you will update the quickstart app to capture and upload images. ## Update the quickstart client app to capture and upload images 1. In Visual Studio, open the Package.appxmanifest file for the Windows app project and in the **Capabilities** tab enable the **Webcam** and **Microphone** capabilities. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-app-manifest-camera.png) This makes sure that your app can use a camera attached to the computer. Users will be requested to allow camera access the first time that the app is run. 2. Repeat the step above for the Windows Phone app project. 3. In the Windows app project, open the MainPage.xaml file and replace the **StackPanel** element directly after the first **QuickStartTask** element with the following code: 2. Replace the **StackPanel** element in the **DataTemplate** with the following code: This adds an image to the **ItemTemplate** and sets its binding source as the URI of the uploaded image in the Blob Storage service. 3. In the Windows Phone app project, open the MainPage.xaml file and replace the **ButtonSave** element with the following code: 2. Replace the **StackPanel** element in the **DataTemplate** with the following code: 4. In the shared DataModel folder, open the TodoItem.cs project file and add add the following properties to the TodoItem class: [JsonProperty(PropertyName = "containerName")] public string ContainerName { get; set; } [JsonProperty(PropertyName = "resourceName")] public string ResourceName { get; set; } [JsonProperty(PropertyName = "sasQueryString")] public string SasQueryString { get; set; } [JsonProperty(PropertyName = "imageUri")] public string ImageUri { get; set; } 3. Open the shared MainPage.cs project file and add the following **using** statements: using Windows.Media.Capture; using Windows.Media.MediaProperties; using Windows.Storage; using Windows.UI.Xaml.Input; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; using Windows.UI.Xaml.Media.Imaging; 5. Add the following code to the MainPage class: // Use a StorageFile to hold the captured image for upload. StorageFile media = null; MediaCapture cameraCapture; bool IsCaptureInProgress; private async Task CaptureImage() { // Capture a new photo or video from the device. cameraCapture = new MediaCapture(); cameraCapture.Failed += cameraCapture_Failed; // Initialize the camera for capture. await cameraCapture.InitializeAsync(); #if WINDOWS_PHONE_APP cameraCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees); cameraCapture.SetRecordRotation(VideoRotation.Clockwise90Degrees); #endif captureGrid.Visibility = Windows.UI.Xaml.Visibility.Visible; previewElement.Visibility = Windows.UI.Xaml.Visibility.Visible; previewElement.Source = cameraCapture; await cameraCapture.StartPreviewAsync(); } private async void previewElement_Tapped(object sender, TappedRoutedEventArgs e) { // Block multiple taps. if (!IsCaptureInProgress) { IsCaptureInProgress = true; // Create the temporary storage file. media = await ApplicationData.Current.LocalFolder .CreateFileAsync("capture_file.jpg", CreationCollisionOption.ReplaceExisting); // Take the picture and store it locally as a JPEG. await cameraCapture.CapturePhotoToStorageFileAsync( ImageEncodingProperties.CreateJpeg(), media); captureButtons.Visibility = Visibility.Visible; // Use the stored image as the preview source. BitmapImage tempBitmap = new BitmapImage(new Uri(media.Path)); imagePreview.Source = tempBitmap; imagePreview.Visibility = Visibility.Visible; previewElement.Visibility = Visibility.Collapsed; IsCaptureInProgress = false; } } private async void cameraCapture_Failed(MediaCapture sender, MediaCaptureFailedEventArgs errorEventArgs) { // It's safest to return this back onto the UI thread to show the message dialog. MessageDialog dialog = new MessageDialog(errorEventArgs.Message); await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await dialog.ShowAsync(); }); } private async void ButtonCapture_Click(object sender, RoutedEventArgs e) { // Prevent multiple initializations. ButtonCapture.IsEnabled = false; await CaptureImage(); } private async void ButtonRetake_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { // Reset the capture and then start again. await ResetCaptureAsync(); await CaptureImage(); } private async void ButtonCancel_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e) { await ResetCaptureAsync(); } private async Task ResetCaptureAsync() { captureGrid.Visibility = Windows.UI.Xaml.Visibility.Collapsed; imagePreview.Visibility = Visibility.Collapsed; captureButtons.Visibility = Visibility.Collapsed; previewElement.Source = null; ButtonCapture.IsEnabled = true; // Make sure we stop the preview and release resources. await cameraCapture.StopPreviewAsync(); cameraCapture.Dispose(); media = null; } This code displays the UI used to capture an image, and saves the image to a storage file. 6. Replace the existing `InsertTodoItem` method with the following code: private async Task InsertTodoItem(TodoItem todoItem) { string errorString = string.Empty; if (media != null) { // Set blob properties of TodoItem. todoItem.ContainerName = "todoitemimages"; // Use a unigue GUID to avoid collisions. todoItem.ResourceName = Guid.NewGuid().ToString(); } // Send the item to be inserted. When blob properties are set this // generates an SAS in the response. await todoTable.InsertAsync(todoItem); // If we have a returned SAS, then upload the blob. if (!string.IsNullOrEmpty(todoItem.SasQueryString)) { // Get the URI generated that contains the SAS // and extract the storage credentials. StorageCredentials cred = new StorageCredentials(todoItem.SasQueryString); var imageUri = new Uri(todoItem.ImageUri); // Instantiate a Blob store container based on the info in the returned item. CloudBlobContainer container = new CloudBlobContainer( new Uri(string.Format("https://{0}/{1}", imageUri.Host, todoItem.ContainerName)), cred); // Get the new image as a stream. using (var inputStream = await media.OpenReadAsync()) { // Upload the new image as a BLOB from the stream. CloudBlockBlob blobFromSASCredential = container.GetBlockBlobReference(todoItem.ResourceName); await blobFromSASCredential.UploadFromStreamAsync(inputStream); } // When you request an SAS at the container-level instead of the blob-level, // you are able to upload multiple streams using the same container credentials. await ResetCaptureAsync(); } // Add the new item to the collection. items.Add(todoItem); } This code sends a request to the mobile service to insert a new TodoItem. The response contains the SAS, which is then used to upload the image from local storage to Azure Blob storage. The URL of the uploaded image is used in data binding. The final step is to test both versions of the app and validate that uploads succeed from both devices. ## Test uploading the images in your app 1. In Visual Studio, make sure that the Windows project is set as the default project, then press the F5 key to run the app. 2. Enter text in the textbox under **Insert a TodoItem**, then click **Photo**. 3. Click or tap the preview to take a picture, then click **Upload** to insert the new item and upload the image. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-quickstart-blob-appbar2.png) 4. The new item, along with the uploaded image, is displayed in the list view. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-quickstart-blob-ie.png) >[AZURE.NOTE]The image is downloaded automatically from Blob storage when the *imageUri* property of the new item is bound to the **Image** control. 5. Stop the app and restart the Windows Phone project version of the app. The previously uploaded image is displayed. 6. As before, enter some text in the textbox, then click **Photo**. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-upload-blob-app-view-wp8.png) 3. Tap the preview to take a picture, then click **Upload** to insert the new item and upload the image. ![](./media/mobile-services-windows-universal-dotnet-upload-to-blob-storage/mobile-upload-blob-app-view-final-wp8.png) You have completed the upload images tutorial. ## Next steps Now that you have been able to securely upload images by integrating your mobile service with the Blob service, check out some of the other backend service and integration topics: + [Schedule backend jobs in Mobile Services] Learn how to use the Mobile Services job scheduler functionality to define server script code that is executed on a schedule that you define. + [Mobile Services server script reference] Reference topics for using server scripts to perform server-side tasks and integration with other Azure components and external resources. + [Mobile Services .NET How-to Conceptual Reference] Learn more about how to use Mobile Services with .NET [Install the Storage Client library]: #install-storage-client [Update the client app to capture images]: #add-select-images [Update the insert script to generate an SAS]: #update-scripts [Upload images to test the app]: #test [Next Steps]:#next-steps [2]: ./media/mobile-services-windows-store-dotnet-upload-data-blob-storage/mobile-add-storage-nuget-package-dotnet.png [Send email from Mobile Services with SendGrid]: store-sendgrid-mobile-services-send-email-scripts.md [Schedule backend jobs in Mobile Services]: mobile-services-schedule-recurring-tasks.md [Send push notifications to Windows Store apps using Service Bus from a .NET back-end]: http://go.microsoft.com/fwlink/?LinkId=277073&clcid=0x409 [Mobile Services server script reference]: mobile-services-how-to-use-server-scripts.md [Get started with Mobile Services]: mobile-services-javascript-backend-windows-store-dotnet-get-started.md [How To Create a Storage Account]: https://azure.microsoft.com/en-us/documentation/articles/storage-create-storage-account/ [Azure Storage Client library for Store apps]: http://go.microsoft.com/fwlink/p/?LinkId=276866 [Mobile Services .NET How-to Conceptual Reference]: mobile-services-windows-dotnet-how-to-use-client-library.md [App settings]: http://msdn.microsoft.com/library/windowsazure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7 ================================================ FILE: docs/mobile-services-manage-command-line-interface.md ================================================ # Automate mobile services with command-line tools >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   ## Overview This topic shows you how to use the Azure command-line tools to automate the creation and management of Azure Mobile Services. This topic shows you how to install and get started using the command-line tools and use them to perform key Mobile Services. When combined into a single script or batch file, these individual commands automate the creation, verification, and deletion process of a mobile service. This topic covers a selection of common administration tasks supported by the Azure command-line tools. For more information, see [Azure command-line tools documentation][reference-docs]. ## Install the Azure Command-Line Tools The following list contains information for installing the command-line tools, depending on your operating system: * **Windows**: Download the [Azure Command-Line Tools Installer][windows-installer]. Open the downloaded .msi file and complete the installation steps as you are prompted. * **Mac**: Download the [Azure SDK Installer][mac-installer]. Open the downloaded .pkg file and complete the installation steps as you are prompted. * **Linux**: Install the latest version of [Node.js][nodejs-org] (see [Install Node.js via Package Manager][install-node-linux]), then run the following command: npm install azure-cli -g To test the installation, type `azure` at the command prompt. When the installation is successful, you will see a list of all the available `azure` commands. ## How to download and import publish settings To get started, you must first download and import your publish settings. Then you can use the tools to create and manage Azure Services. To download your publish settings, use the `account download` command: azure account download This opens your default browser and prompts you to sign in to the Azure classic portal. After signing in, your `.publishsettings` file is downloaded. Note the location of this saved file. Next, import the `.publishsettings` file by running the following command, replacing `` with the path to your `.publishsettings` file: azure account import You can remove all of the information stored by the import command by using the account clear command: azure account clear To see a list of options for `account` commands, use the `-help` option: azure account -help After importing your publish settings, you should delete the `.publishsettings` file for security reasons. For more information, see [How to install the Azure Command-Line Tools for Mac and Linux]. You are now ready to begin creating and managing Azure Mobile Services from the command line or in batch files. ## How to create a mobile service You can use the command-line tools to create a new mobile service instance. While creating the mobile service, you also create a SQL Database instance in a new server. The following command creates a new mobile service instance in your subscription, where `` is the name of the new mobile service, `` is the login name of the new server, and `` is the password for the new login: azure mobile create The `mobile create` command fails when the specified mobile service exists. In your automation scripts, you should attempt to delete a mobile service before attempting to recreate it. ## How to list existing mobile services in a subscription > [AZURE.NOTE] Commands in the CLI related to "list" and "script" only work with the JavaScript backend. The following command returns a list of all the mobile services in an Azure subscription: azure mobile list This command also shows the current state and URL of each mobile service. ## How to delete an existing mobile service You can use the command-line tools to delete an existing mobile service, along with the related SQL Database and server. The following command deletes the mobile service, where `` is the name of the mobile service to delete: azure mobile delete -a -q By including `-a` and `-q` parameters, this command also deletes the SQL Database and server used by the mobile service without displaying a prompt. > [AZURE.NOTE] If you do not specify the -q parameter along with -a or -d, execution is paused and you are prompted to select delete options for your SQL Database. Only use the -a parameter when no other service uses the database or server; otherwise use the -d parameter to only delete data that belongs to the mobile service being deleted. ## How to create a table in the mobile service The following command creates a table in the specified mobile service, where `` is the name of the mobile service and `` is the name of the table to create: azure mobile table create This creates a new table with the default permissions, `application`, for the table operations: `insert`, `read`, `update`, and `delete`. The following command creates a new table with public `read` permission but with `delete` permission granted only to administrators: azure mobile table create -p read=public,delete=admin The following table shows the script permission value compared to the permission value in the [Azure classic portal]. |Script value|Portal value| |========|========| |`public`|Everyone| |`application`(default)|Anybody with the application key| |`user`|Only authenticated users| |`admin`|Only scripts and admins| The `mobile table create` command fails when the specified table already exists. In your automation scripts, you should attempt to delete a table before attempting to recreate it. ## How to list existing tables in a mobile service The following command returns a list of all of the tables in a mobile service, where `` is the name of the mobile service: azure mobile table list This command also shows the number of indexes on each table and the number of data rows currently in the table. ## How to delete an existing table from the mobile service The following command deletes a table from the mobile service, where `` is the name of the mobile service and `` is the name of the table to delete: azure mobile table delete -q In automation scripts, use the `-q` parameter to delete the table without displaying a confirmation prompt that blocks execution. ## How to register a script to a table operation The following command uploads and registers a function to an operation on a table, where `` is the name of the mobile service, `` is the name of the table, and `` is the table operation, which can be `read`, `insert`, `update`, or `delete`: azure mobile script upload table/..js Note that this operation uploads a JavaScript (.js) file from the local computer. The name of the file must be composed from the table and operation names, and it must be located in the `table` subfolder relative to the location where the command is executed. For example, the following operation uploads and registers a new `insert` script that belongs to the `TodoItems` table: azure mobile script upload todolist table/todoitems.insert.js The function declaration in the script file must also match the registered table operation. This means that for an `insert` script, the uploaded script contains a function with the following signature: function insert(item, user, request) { ... } For more information about registering scripts, see [Mobile Services server script reference]. [Download and install the command-line tools]: #install [Download and import publish settings]: #import [Create a new mobile service]: #create-service [Get the master key]: #get-master-key [Create a new table]: #create-table [Register a new table script]: #register-script [Delete an existing table]: #delete-table [Delete an existing mobile service]: #delete-service [Test the mobile service]: #test-service [List mobile services]: #list-services [List tables]: #list-tables [Next steps]: #next-steps [Mobile Services server script reference]: http://go.microsoft.com/fwlink/p?LinkId=262293 [Azure classic portal]: https://manage.windowsazure.com/ [nodejs-org]: http://nodejs.org/ [install-node-linux]: https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager [mac-installer]: http://go.microsoft.com/fwlink/p?LinkId=252249 [windows-installer]: http://go.microsoft.com/fwlink/p?LinkID=275464 [reference-docs]: http://azure.microsoft.com/documentation/articles/virtual-machines-command-line-tools/#Commands_to_manage_mobile_services [How to install the Azure Command-Line Tools for Mac and Linux]: http://go.microsoft.com/fwlink/p/?LinkId=275795 ================================================ FILE: docs/mobile-services-schedule-recurring-tasks.md ================================================ # Schedule recurring jobs in Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [.NET backend](mobile-services-dotnet-backend-schedule-recurring-tasks.md) - [Javascript backend](mobile-services-schedule-recurring-tasks.md) This topic shows you how to use the job scheduler functionality in the Azure classic portal to define server script code that is executed based on a schedule that you define. In this case, the script periodically check with a remote service, in this case Twitter, and stores the results in a new table. Some other periodic tasks that can be scheduled include: + Archiving old or duplicate data records. + Requesting and storing external data, such as tweets, RSS entries, and location information. + Processing or resizing stored images. This tutorial shows you how to use the job scheduler to create a scheduled job that requests tweet data from Twitter and stores the tweets in a new Updates table. ## Register for access to Twitter v1.1 APIs and store credentials The new Twitter v1.1 APIs requires your app to authenticate before accessing resources. First, you need to get the credentials needed to request access by using OAuth 2.0. Then, you will store them securely in the app settings for your mobile service. 1. If you haven't already done so, complete the steps in the topic [Register your apps for Twitter login with Mobile Services](./ mobile-services-how-to-register-twitter-authentication.md). Twitter generates the credentials needed to enable you to access Twitter v1.1 APIs. You can get these credentials from the Twitter Developers website. 2. Navigate to the [Twitter Developers](http://go.microsoft.com/fwlink/p/?LinkId=268300) website, sign-in with your Twitter account credentials and select your Twitter app. 3. In the **Keys and Access Tokens** tab for the app, make a note of the following values: + **Consumer key** + **Consumer secret** + **Access token** + **Access token secret** 4. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your mobile service. 5. Click the **Identity** tab, enter the **Consumer key** and **Consumer secret** values obtained from Twitter, and click **Save**. ![](./media/mobile-services-register-twitter-access/mobile-identity-tab-twitter-only.png) 2. Click the **Configure** tab, scroll down to **App settings** and enter a **Name** and **Value** pair for each of the following that you obtained from the Twitter site, then click **Save**. + `TWITTER_ACCESS_TOKEN` + `TWITTER_ACCESS_TOKEN_SECRET` ![](./media/mobile-services-register-twitter-access/mobile-schedule-job-app-settings.png) This stores the Twitter access token in app settings. Like the consumer credentials on the **Identity** tab, the access credentials are also stored encrypted in app settings, and you can access them in your server scripts without hard-coding them in the script file. For more information, see [App settings]. [Mobile Services server script reference]: http://go.microsoft.com/fwlink/?LinkId=262293 [Register your apps for Twitter login with Mobile Services]: ./ mobile-services-how-to-register-twitter-authentication.md [Twitter Developers]: http://go.microsoft.com/fwlink/p/?LinkId=268300 [App settings]: http://msdn.microsoft.com/library/azure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7 ## Create the new Updates table Next, you need to create a new table in which to store tweets. 2. In the [Azure classic portal], click the **Data** tab for your mobile service, then click **+Create**. 3. In **Table name** type _Updates_, then click the check button. ## Create a new scheduled job Now, you can create the scheduled job that accesses Twitter and stores tweet data in the new Updates table. 2. Click the **Scheduler** tab, then click **+Create**. >[AZURE.NOTE]When you run your mobile service in Free tier, you are only able to run one scheduled job at a time. In paid tiers, you can run up to ten scheduled jobs at a time. 3. In the scheduler dialog, enter _getUpdates_ for the **Job Name**, set the schedule interval and units, then click the check button. This creates a new job named **getUpdates**. 4. Click the new job you just created, click the **Script** tab and replace the placeholder function **getUpdates** with the following code: var updatesTable = tables.getTable('Updates'); var request = require('request'); var twitterUrl = "https://api.twitter.com/1.1/search/tweets.json?q=%23mobileservices&result_type=recent"; // Get the service configuration module. var config = require('mobileservice-config'); // Get the stored Twitter consumer key and secret. var consumerKey = config.twitterConsumerKey, consumerSecret = config.twitterConsumerSecret // Get the Twitter access token from app settings. var accessToken= config.appSettings.TWITTER_ACCESS_TOKEN, accessTokenSecret = config.appSettings.TWITTER_ACCESS_TOKEN_SECRET; function getUpdates() { // Check what is the last tweet we stored when the job last ran // and ask Twitter to only give us more recent tweets appendLastTweetId( twitterUrl, function twitterUrlReady(url){ // Create a new request with OAuth credentials. request.get({ url: url, oauth: { consumer_key: consumerKey, consumer_secret: consumerSecret, token: accessToken, token_secret: accessTokenSecret }}, function (error, response, body) { if (!error && response.statusCode == 200) { var results = JSON.parse(body).statuses; if(results){ console.log('Fetched ' + results.length + ' new results from Twitter'); results.forEach(function (tweet){ if(!filterOutTweet(tweet)){ var update = { twitterId: tweet.id, text: tweet.text, author: tweet.user.screen_name, date: tweet.created_at }; updatesTable.insert(update); } }); } } else { console.error('Could not contact Twitter'); } }); }); } // Find the largest (most recent) tweet ID we have already stored // (if we have stored any) and ask Twitter to only return more // recent ones function appendLastTweetId(url, callback){ updatesTable .orderByDescending('twitterId') .read({success: function readUpdates(updates){ if(updates.length){ callback(url + '&since_id=' + (updates[0].twitterId + 1)); } else { callback(url); } }}); } function filterOutTweet(tweet){ // Remove retweets and replies return (tweet.text.indexOf('RT') === 0 || tweet.to_user_id); } This script calls the Twitter query API using stored credentials to request recent tweets that contain the hashtag `#mobileservices`. Duplicate tweets and replies are removed from the results before they are stored in the table. >[AZURE.NOTE]This sample assumes that only a few rows are inserted into the table during each scheduled run. In cases where many rows are inserted in a loop you may run out of connections when running on the Free tier. In this case, you should perform inserts in batches. For more information, see [How to: Perform bulk inserts](mobile-services-how-to-use-server-scripts.md#bulk-inserts). 6. Click **Run Once** to test the script. This saves and executes the job while it remains disabled in the scheduler. 7. Click the back button, click **Data**, click the **Updates** table, click **Browse**, and verify that Twitter data has been inserted into the table. 8. Click the back button, click **Scheduler**, select **getUpdates**, then click **Enable**. This enables the job to run on the specified schedule, in this case every hour. Congratulations, you have successfully created a new scheduled job in your mobile service. This job will be executed as scheduled until you disable or modify it. ## See also * [Mobile Services server script reference]
Learn more about registering and using server scripts. [Register for Twitter access and store credentials]: #get-oauth-credentials [Create the new Updates table]: #create-table [Create a new scheduled job]: #add-job [Next steps]: #next-steps [Mobile Services server script reference]: http://go.microsoft.com/fwlink/?LinkId=262293 [WindowsAzure.com]: http://www.windowsazure.com/ [Azure classic portal]: https://manage.windowsazure.com/ [Register your apps for Twitter login with Mobile Services]: https://azure.microsoft.com/develop/mobile/how-to-guides/register-for-twitter-authentication [Twitter Developers]: http://go.microsoft.com/fwlink/p/?LinkId=268300 [App settings]: http://msdn.microsoft.com/library/windowsazure/b6bb7d2d-35ae-47eb-a03f-6ee393e170f7 ================================================ FILE: docs/mobile-services-sql-scale-guidance.md ================================================ # Scale mobile services backed by Azure SQL Database >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   Azure Mobile Services makes it very easy to get started and build an app that connects to a cloud-hosted backend that stores data in a SQL database. As your app grows, scaling your service instances is as simple as adjusting scale settings in the portal to add more computational and networking capacity. However, scaling the SQL database backing your service requires some proactive planning and monitoring as the service receives more load. This document will walk you through a set of best practices to ensure continued great performance of your SQL-backed mobile services. This topic walks you through these basic sections: 1. [Diagnosing Problems](#Diagnosing) 2. [Indexing](#Indexing) 3. [Schema Design](#Schema) 4. [Query Design](#Query) 5. [Service Architecture](#Architecture) 6. [Advanced Troubleshooting](#Advanced) ## Diagnosing Problems If you suspect your mobile service is experiencing problems under load, the first place to check is the **Dashboard** tab for your service in the [Azure classic portal]. Some of the things to verify here: - Your usage meters including **API Calls** and **Active Devices** meters are not over quota - **Endpoint Monitoring** status indicates service is up (only available if service is using the Standard tier and Endpoint Monitoring is enabled) If any of the above is not true, consider adjusting your scale settings on the *Scale* tab. If that does not address the issue, you can proceed and investigate whether Azure SQL Database may be the source of the issue. The next few sections cover a few different approaches to diagnose what may be going wrong. ### Choosing the Right SQL Database Tier It is important to understand the different database tiers you have at your disposal to ensure you've picked the right tier given your app's needs. Azure SQL Database offers three different service tiers: - Basic - Standard - Premium Here are some recommendations on selecting the right tier for your database: - **Basic** - use at development time or for small production services where you only expect to make a single database query at a time - **Standard** - use for production services where you expect multiple concurrent database queries - **Premium** - use for large scale production services with many concurrent queries, high peak load, and expected low latency for every request. For more information on when to use each tier, see [Reasons to Use the New Service Tiers] ### Analyzing Database Metrics Once you are familiar with the different database tiers, we can explore database performance metrics to help us reason about scaling within and among the tiers. 1. Launch the [Azure classic portal]. 2. On the Mobile Services tab, select the service you want to work with. 3. Select the **Configure** tab. 4. Select the **SQL Database** name in the **Database Settings** section. This will navigate to the Azure SQL Database tab in the portal. 5. Navigate to the **Monitor** tab 6. Ensure the relevant metrics are displayed by using the **Add Metrics** button. Include the following - *CPU Percentage* (available only in Basic/Standard/Premium tiers) - *Data IO Percentage* (available only in Basic/Standard/Premium tiers) - *Log IO Percentage* (available only in Basic/Standard/Premium tiers) - *Storage* 7. Inspect the metrics over the time window when your service was experiencing issues. ![Azure classic portal - SQL Database Metrics][PortalSqlMetrics] If any metric exceeds 80% utilization for an extended period of time, this could indicate a performance problem. For more detailed information on understanding database utilization, see [Understanding Resource Use](http://msdn.microsoft.com/library/azure/dn369873.aspx#Resource). If the metrics indicate your database is incurring high utilization, consider **scaling up the database to a higher service tier** as a first mitigation step. To immediately resolve issues, consider using the **Scale** tab for your database to scale up your database. This will result in an increase in your bill. ![Azure classic portal - SQL Database Scale][PortalSqlScale] As soon as possible, consider these additional mitigation steps: - **Tune your database.** It is frequently possible to reduce database utilization and avoid having to scale to a higher tier by optimizing your database. - **Consider your service architecture.** Frequently your service load is not distributed evenly over time but contains "spikes" of high demand. Instead of scaling the database up to handle the spikes, and having it go underutilized during periods of low demand, it is frequently possible to adjust the service architecture to avoid such spikes, or to handle them without incurring database hits. The remaining sections of this document contain tailored guidance to help with implementing these mitigations. ### Configuring Alerts It is frequently useful to configure alerts for key database metrics as a proactive step to ensure you have plenty of time to react in case of resource exhaustion. 1. Navigate to the **Monitoring** tab for the database you want to set up alerts for 2. Ensure the relevant metrics are displayed as described in the previous section 3. Select the metric you want to set an alert for and select **Add Rule** ![Azure Management Portal - SQL Alert][PortalSqlAddAlert] 4. Provide a name and description for the alert ![Azure Management Portal - SQL Alert Name and Description][PortalSqlAddAlert2] 5. Specify the value to use as the alert threshold. Consider using **80%** to allow for some reaction time. Also be sure to specify an email address that you actively monitor. ![Azure Management Portal - SQL Alert Threshold and Email][PortalSqlAddAlert3] For more information on diagnosing SQL issues, see [Advanced Diagnostics](#AdvancedDiagnosing) at the bottom of this document. ## Indexing When you start to see problems with your query performance, the first thing you should investigate is the design of your indexes. Indexes are important because they directly affect how the SQL engine executes a query.  For instance, if you often need to look up an element by a certain field, you should consider adding an index for that column. Otherwise, the SQL engine will be forced to perform a table scan and read each physical record (or at least the query column) and the records could be substantially spread out on disk. So, if you are frequently doing WHERE or JOIN statements on particular columns, you should make sure you index them. See the section [Creating Indexes](#CreatingIndexes) for more information. If indexes are so great and table scans are so bad, does that mean you should index every column in your table, just to be safe? The short answer is, "probably not." Indexes take up space and have overhead themselves: every time there is an insert in a table, the index structures for each of the indexed columns need to be updated. See below for guidelines on how to choose your column indexes. ### Index Design Guidelines As mentioned above, it's not always better to add more indexes to a table, because indexes themselves can be costly, both in terms of performance and storage overhead. #### Query considerations - Consider adding indexes to columns that are frequently used in predicates (e.g., WHERE clauses) and join conditions, while balancing the database considerations below. - Write queries that insert or modify as many rows as possible in a single statement, instead of using multiple queries to update the same rows. When there is only one statement, the database engine can better optimize how it maintains indexes. #### Database considerations Large numbers of indexes on a table affect the performance of INSERT, UPDATE, DELETE, and MERGE statements because all indexes must be adjusted appropriately as data in the table changes. - For **heavily updated** tables, avoid indexing heavily updated columns. - For tables that are **not frequently updated** but that have large volumes of data, use many indexes. This can improve the performance of queries that do not modify data (such as SELECT statements) because the query optimizer will have more options for finding the best access method. Indexing small tables may not be optimal because it can take the query optimizer longer to traverse the index searching for data than to perform a simple table scan. Therefore, indexes on small tables might never be used, but must still be maintained as data in the table changes. ### Creating Indexes #### JavaScript backend To set the index for a column in the JavaScript backend, do the following: 1. Open your mobile service in the [Azure classic portal]. 2. Click the **Data** tab. 3. Select the table you want to modify. 4. Click the **Columns** tab. 5. Select the column. In the command bar, click **Set Index**: ![Mobile Services Portal - Set Index][SetIndexJavaScriptPortal] You can also remove indexes within this view. #### .NET backend To define an index in Entity Framework, use the attribute `[Index]` on the fields that you want to index. For example: public class TodoItem : EntityData { public string Text { get; set; } [Index] public bool Complete { get; set; } } For more information on indexes, see [Index Annotations in Entity Framework][]. For further tips on optimizing indexes, see [Advanced Indexing](#AdvancedIndexing) at the bottom of this document. ## Schema Design Here are a few issues to be aware of when picking the data types for your objects, which in turn translates to the schema of your SQL database. Tuning the schema can frequently bring significant performance improvements since SQL has custom optimized ways of handling indexing and storage for different data types: - **Use the provided ID column**. Every mobile service table comes with a default ID column configured as the primary key and has an index set on it. There is no need to create an additional ID column. - **Use the correct datatypes in your model.** If you know a certain property of your model will be a numeric or boolean, be sure to define it that way in your model instead of as a string. In the JavaScript backend, use literals such as `true` instead of `"true"` and `5` instead of `"5"`. In the .NET backend, use the `int` and `bool` types when you declare the properties of your model. This enables SQL to create the correct schema for those types, which makes queries more efficient. ## Query Design Here are some guidelines to consider when querying the database: - **Always execute join operations in the database.** Frequently you will need to combine records from two or more tables where the records being combined share a common field (also known as a *join*). This operation can be inefficient if performed incorrectly since it may involve pulling down all the entities from both tables and then iterating through all of them. This kind of operation is best left to the database itself, but it is sometimes easy to mistakenly perform it on the client or in the mobile service code. - Don't perform joins in your app code - Don't perform joins in your mobile service code. When using the JavaScript backend, be aware that the [table object](http://msdn.microsoft.com/library/windowsazure/jj554210.aspx) does not handle joins. Be sure to use the [mssql object](http://msdn.microsoft.com/library/windowsazure/jj554212.aspx) directly to ensure the join happens in the database. For more information, see [Join relational tables](mobile-services-how-to-use-server-scripts.md#joins). If using the .NET backend and querying via LINQ, joins are automatically handled at the database level by Entity Framework. - **Implement paging.** Querying the database can sometimes result in a large number of records being returned to the client. To minimize the size and latency of operations, consider implementing paging. - By default your mobile service will limit any incoming queries to a page size of 50, and you can manually request up to 1,000 records. For more information, see "Return data in pages" for [Windows Store](mobile-services-windows-dotnet-how-to-use-client-library.md#paging), [iOS](mobile-services-ios-how-to-use-client-library.md#paging), [Android](mobile-services-android-how-to-use-client-library.md#paging), [HTML/JavaScript](mobile-services-html-how-to-use-client-library#paging), and [Xamarin](partner-xamarin-mobile-services-how-to-use-client-library.md#paging). - There is no default page size for queries made from your mobile service code. If your app does not implement paging, or as a defensive measure, consider applying default limits to your queries. In the JavaScript backend, use the **take** operator on the [query object](http://msdn.microsoft.com/library/azure/jj613353.aspx). If using the .NET backend, consider using the [Take method] as part of your LINQ query. For more information on improving query design, including how to analyze query plans, see [Advanced Query Design](#AdvancedQuery) at the bottom of this document. ## Service Architecture Imagine a scenario where you are about to send a push notification to all your customers to check out some new content in your app. As they tap on the notification, the app launches, which possibly triggers a call to your mobile service and a query execution against your SQL database. As potentially millions of customers take this action over the span of just a few minutes, this will generate a surge of SQL load, which may be orders of magnitude higher than your app's steady state load. This could be addressed by scaling your app to a higher SQL tier during the spike and then scaling it back down, however that solution requires manual intervention and comes with increased cost. Frequently slight tweaks in your mobile service architecture can significantly balance out the load clients drive to your SQL database and eliminate problematic spikes in demand. These modifications can often be implemented easily with minimal impact to your customer's experience. Here are some examples: - **Spread out the load over time.** If you control the timing of certain events (for example, a broadcast push notification), which are expected to generate a spike in demand, and the timing of those events is not critical, consider spreading them out over time. In the example above, perhaps it is acceptable for your app customers to get notified of the new app content in batches over the span of a day instead of nearly simultaneously. Consider batching up your customers into groups which will allow staggered delivery to each batch. If using Notification Hubs, applying an additional tag to track the batch, and then delivering a push notification to that tag provides an easy way to implement this strategy. For more information on tags, see [Use Notification Hubs to send breaking news](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-windows-notification-dotnet-push-xplat-segmented-wns/). - **Use Blob and Table Storage whenever appropriate.** Frequently the content that the customers will view during the spike is fairly static and doesn't need to be stored in a SQL database since you are unlikely to need relational querying capabilities over that content. In that case, consider storing the content in Blob or Table Storage. You can access public blobs in Blob Storage directly from the device. To access blobs in a secure way or use Table Storage, you will need to go through a Mobile Services Custom API in order to protect your storage access key. For more information, see [Upload images to Azure Storage by using Mobile Services](mobile-services-dotnet-backend-windows-universal-dotnet-upload-data-blob-storage.md). - **Use an in-memory cache**. Another alternative is to store data, which would commonly be accessed during a traffic spike, in an in-memory cache such as [Azure Cache](https://azure.microsoft.com/services/cache/). This means incoming requests would be able to fetch the information they need from memory, instead of repeatedly querying the database. ## Advanced Troubleshooting This section covers some more advanced diagnostic tasks, which may be useful if the steps so far have not addressed the issue fully. ### Prerequisites To perform some of the diagnostic tasks in this section, you need access to a management tool for SQL databases such as **SQL Server Management Studio** or the management functionality built into the **Azure classic portal**. SQL Server Management Studio is a free Windows application, which offers the most advanced capabilities. If you do not have access to a Windows machine (for example if you are using a Mac), consider provisioning a Virtual Machine in Azure as shown in [Create a Virtual Machine Running Windows Server](https://azure.microsoft.com/en-us/documentation/articles/virtual-machines-windows-hero-tutorial/) and then connecting remotely to it. If you intend to use the VM primarily for the purpose of running SQL Server Management Studio, a **Basic A0** (formerly "Extra Small") instance should be sufficient. The Azure classic portal offers a built-in management experience, which is more limited, but is available without a local install. The following steps walk you through obtaining the connection information for the SQL database backing your mobile service and then using either of the two tools to connect to it. You may pick whichever tool you prefer. #### Obtain SQL connection information 1. Launch the [Azure classic portal]. 2. On the Mobile Services tab, select the service you want to work with. 3. Select the **Configure** tab. 4. Select the **SQL Database** name in the **Database Settings** section. This will navigate to the Azure SQL Database tab in the portal. 5. Select **Set up Azure firewall rules for this IP address**. 6. Make a note of the server address in the **Connect to your database** section, for example: *mcml4otbb9.database.windows.net*. #### SQL Server Management Studio 1. Navigate to [SQL Server Editions - Express](http://www.microsoft.com/server-cloud/products/sql-server-editions/sql-server-express.aspx) 2. Find the **SQL Server Management Studio** section and select the **Download** button underneath. 3. Complete the setup steps until you can successfully run the application: ![SQL Server Management Studio][SSMS] 4. In the **Connect to Server** dialog enter the following values - Server name: *server address you obtained earlier* - Authentication: *SQL Server Authentication* - Login: *login you picked when creating server* - Password: *password you picked when creating server* 5. You should now be connected. #### SQL Database Management Portal 1. On Azure SQL Database tab for your database, select the **Manage** button 2. Configure the connection with the following values - Server: *should be pre-set to the right value* - Database: *leave blank* - Username: *login you picked when creating server* - Password: *password you picked when creating server* 3. You should now be connected. ![Azure classic portal - SQL Database][PortalSqlManagement] ### Advanced Diagnostics A lot of diagnostic tasks can be completed easily right in the **Azure classic portal**, however some advanced diagnostic tasks are only possible via **SQL Server Management Studio** or the **SQL Database Management Portal**. We will take advantage of dynamic management views, a set of views populated automatically with diagnostic information about your database. This section provides a set of queries we can run against these views to examine various metrics. For more information, see [Monitoring SQL Database Using Dynamic Management Views][]. After completing the steps in the previous section to connect to your database in SQL Server Management Studio, select your database in **Object Explorer**. Expanding **Views** and then **System Views** reveals a list of the management views. To execute the queries below, select **New Query**, while you have selected your database in **Object Explorer**, then paste the query and select **Execute**. ![SQL Server management Studio - dynamic management views][SSMSDMVs] Alternatively if you are using the SQL Database Management Portal, first select your database and then pick **New Query**. ![SQL Database Management Portal - new query][PortalSqlManagementNewQuery] To execute any of the queries below, paste it into the window and select **Run**. ![SQL Database Management Portal - run query][PortalSqlManagementRunQuery] #### Advanced Metrics The management portal makes certain metrics readily available if using the Basic, Standard, and Premium tiers. It is easy to obtain these and other metrics using the **[sys.resource\_stats](http://msdn.microsoft.com/library/dn269979.aspx)** management view, regardless of what tier you're using. Consider the following query: SELECT TOP 10 * FROM sys.resource_stats WHERE database_name = 'todoitem_db' ORDER BY start_time DESC > [AZURE.NOTE] > Please execute this query on the **master** database on your server, the **sys.resource\_stats** view is only present on that database. The result will contain the following useful metrics: CPU (% of tier limit), Storage (megabytes), Physical Data Reads (% of tier limit), Log Writes (% of tier limit), Memory (% of tier limit), Worker Count, Session Count, etc. #### SQL connectivity events The **[sys.event\_log](http://msdn.microsoft.com/library/azure/jj819229.aspx)** view contains the details of connectivity-related events. select * from sys.event_log where database_name = 'todoitem_db' and event_type like 'throttling%' order by start_time desc > [AZURE.NOTE] > Please execute this query on the **master** database on your server, the **sys.event\_log** view is only present on that database. ### Advanced Indexing A table or view can contain the following types of indexes: - **Clustered**. A clustered index specifies how records are physically stored on disk. There must be only one clustered index per table, because the data rows themselves can be sorted in only one order. - **Nonclustered**. Nonclustered indexes are stored separately from data rows and are used to do a lookup based on the index value. All nonclustered indexes on a table use the key values from the clustered index as lookup key. To provide a real-world analogy: consider a book or a technical manual. The contents of each page are a record, the page number is the clustered index, and the topic index in the back of the book is a nonclustered index. Each entry in the topic index points to the clustered index, the page number. > [AZURE.NOTE] > By default, the JavaScript backend of Azure Mobile Services sets **\_createdAt** as the clustered index. If you remove this column, or if you want a different clustered index, be sure to follow the [clustered index design guidelines](#ClusteredIndexes) below. In the .NET backend, the class `EntityData` defines `CreatedAt` as a clustered index using the annotation `[Index(IsClustered = true)]`. #### Clustered index design guidelines Every table should have a clustered index on the column (or columns, in the case of a composite key) with the following properties: - Narrow - uses a small datatype, or is a [composite key][Primary and Foreign Key Constraints] of a small number of narrow columns - Unique, or mostly unique - Static - value is not frequently changed - Ever-increasing - (Optional) Fixed-width - (Optional) nonnull The reason for the **narrow** property is that all other indexes on a table use the key values from the clustered index as lookup keys. In the example of a topic index at the back of a book, the clustered index is a page number and is a small number. If the chapter title were instead included in the clustered index, then the topic index would itself be much longer, because the key value would then be (chapter name, page number). The key should be **static** and **ever-increasing** to avoid having to maintain the physical location of the records (which means either moving records physically, or potentially fragmenting storage by splitting the pages where the records are stored). The clustered index will be most valuable for queries that do the following: - Return a range of values by using operators such as BETWEEN, >, >=, <, and <=. - After the row with the first value is found by using the clustered index, rows with subsequent indexed values are guaranteed to be physically adjacent. - Use JOIN clauses; typically these are foreign key columns. - Use ORDER BY, or GROUP BY clauses. - An index on the columns specified in the ORDER BY or GROUP BY clause may remove the need for the Database Engine to sort the data, because the rows are already sorted. This improves query performance. #### Creating clustered indexes in Entity Framework To set the clustered index in the .NET backend using Entity Framework, set the `IsClustered` property of the annotation. For example, this is the definition of `CreatedAt` in `Microsoft.WindowsAzure.Mobile.Service.EntityData`: [Index(IsClustered = true)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [TableColumnAttribute(TableColumnType.CreatedAt)] public DateTimeOffset? CreatedAt { get; set; } #### Creating indexes in the database schema For the JavaScript backend, you can only modify the clustered index of a table by changing the database schema directly, either through SQL Server Management Studio or the Azure SQL Database Portal. The following guides describe how to set a clustered or nonclustered index by modifying the database schema directly: - [Creating and Modifying PRIMARY KEY Constraints][] - [Create Nonclustered Indexes][] - [Create Clustered Indexes][] - [Create Unique Indexes][] #### Find top N missing indexes You can write SQL queries on dynamic management views that will tell you more detailed information about the resource usage of individual queries or give you heuristics on what indexes to add. The following query determines which 10 missing indexes would produce the highest anticipated cumulative improvement, in descending order, for user queries. SELECT TOP 10 * FROM sys.dm_db_missing_index_group_stats ORDER BY avg_total_user_cost * avg_user_impact * (user_seeks + user_scans) DESC; The following example query runs a join across these tables to get a list of the columns that should be part of each missing index and calculates an 'index advantage' to determine if the given index should be considered: SELECT * from ( SELECT (user_seeks+user_scans) * avg_total_user_cost * (avg_user_impact * 0.01) AS index_advantage, migs.* FROM sys.dm_db_missing_index_group_stats migs ) AS migs_adv, sys.dm_db_missing_index_groups mig, sys.dm_db_missing_index_details mid WHERE migs_adv.group_handle = mig.index_group_handle and mig.index_handle = mid.index_handle AND migs_adv.index_advantage > 10 ORDER BY migs_adv.index_advantage DESC; For more information, see [Monitoring SQL Database Using Dynamic Management Views][] and [Missing Index Dynamic Management Views][]. ### Advanced Query Design Frequently it's difficult to diagnose what queries are most expensive for the database. #### Finding top N queries The following example returns information about the top five queries ranked by average CPU time. This example aggregates the queries according to their query hash, so that logically equivalent queries are grouped by their cumulative resource consumption. SELECT TOP 5 query_stats.query_hash AS "Query Hash", SUM(query_stats.total_worker_time) / SUM(query_stats.execution_count) AS "Avg CPU Time", MIN(query_stats.statement_text) AS "Statement Text" FROM (SELECT QS.*, SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(st.text) ELSE QS.statement_end_offset END - QS.statement_start_offset)/2) + 1) AS statement_text FROM sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as query_stats GROUP BY query_stats.query_hash ORDER BY 2 DESC; For more information, see [Monitoring SQL Database Using Dynamic Management Views][]. In addition to executing the query, the **SQL Database Management Portal** gives you a nice shortcut to see this data, by selecting **Summary** for your database and then selecting **Query Performance**: ![SQL Database Management Portal - query performance][PortalSqlManagementQueryPerformance] #### Analyzing the query plan Once you have identified expensive queries or if you are about to deploy code using new queries and you would like to investigate their performance, the tooling offers great support for analyzing the **query plan**. The query plan enables you to see what operations take up the builk of CPU time and IO resources when a given SQL query runs. To analyze the query plan in **SQL Server Management Studio**, use the highlighted toolbar buttons. ![SQL Server Management Studio - query plan][SSMSQueryPlan] To analyze the query plan in the **SQL Database Management Portal**, use the highlighted toolbar buttons. ![SQL Database Management Portal - query plan][PortalSqlManagementQueryPlan] ## See Also - [Azure SQL Database Documentation][] - [Azure SQL Database performance and scaling][] - [Troubleshooting Azure SQL Database][] ### Indexing - [Index Basics][] - [General Index Design Guidelines][] - [Unique Index Design Guidelines][] - [Clustered Index Design Guidelines][] - [Primary and Foreign Key Constraints][] - [How much does that key cost?][] ### Entity Framework - [Performance Considerations for Entity Framework 5][] - [Code First Data Annotations][] [SSMS]: ./media/mobile-services-sql-scale-guidance/1.png [PortalSqlManagement]: ./media/mobile-services-sql-scale-guidance/2.png [PortalSqlMetrics]: ./media/mobile-services-sql-scale-guidance/3.png [PortalSqlScale]: ./media/mobile-services-sql-scale-guidance/4.png [PortalSqlAddAlert]: ./media/mobile-services-sql-scale-guidance/5.png [PortalSqlAddAlert2]: ./media/mobile-services-sql-scale-guidance/6.png [PortalSqlAddAlert3]: ./media/mobile-services-sql-scale-guidance/7.png [SetIndexJavaScriptPortal]: ./media/mobile-services-sql-scale-guidance/set-index-portal-ui.png [SSMSDMVs]: ./media/mobile-services-sql-scale-guidance/8.png [PortalSqlManagementNewQuery]: ./media/mobile-services-sql-scale-guidance/9.png [PortalSqlManagementRunQuery]: ./media/mobile-services-sql-scale-guidance/10.png [PortalSqlManagementQueryPerformance]: ./media/mobile-services-sql-scale-guidance/11.png [SSMSQueryPlan]: ./media/mobile-services-sql-scale-guidance/12.png [PortalSqlManagementQueryPlan]: ./media/mobile-services-sql-scale-guidance/13.png [Azure classic portal]: http://manage.windowsazure.com [Azure SQL Database Documentation]: http://azure.microsoft.com/documentation/services/sql-database/ [Managing SQL Database using SQL Server Management Studio]: http://go.microsoft.com/fwlink/p/?linkid=309723&clcid=0x409 [Monitoring SQL Database Using Dynamic Management Views]: http://go.microsoft.com/fwlink/p/?linkid=309725&clcid=0x409 [Azure SQL Database performance and scaling]: http://go.microsoft.com/fwlink/p/?linkid=397217&clcid=0x409 [Troubleshooting Azure SQL Database]: http://msdn.microsoft.com/library/azure/ee730906.aspx [Reasons to Use the New Service Tiers]:http://msdn.microsoft.com/library/azure/dn369873.aspx#Reasons [Take method]:http://msdn.microsoft.com/library/vstudio/bb503062(v=vs.110).aspx [Creating and Modifying PRIMARY KEY Constraints]: http://technet.microsoft.com/library/ms181043(v=sql.105).aspx [Create Clustered Indexes]: http://technet.microsoft.com/library/ms186342(v=sql.120).aspx [Create Unique Indexes]: http://technet.microsoft.com/library/ms187019.aspx [Create Nonclustered Indexes]: http://technet.microsoft.com/library/ms189280.aspx [Primary and Foreign Key Constraints]: http://msdn.microsoft.com/library/ms179610(v=sql.120).aspx [Index Basics]: http://technet.microsoft.com/library/ms190457(v=sql.105).aspx [General Index Design Guidelines]: http://technet.microsoft.com/library/ms191195(v=sql.105).aspx [Unique Index Design Guidelines]: http://technet.microsoft.com/library/ms187019(v=sql.105).aspx [Clustered Index Design Guidelines]: http://technet.microsoft.com/library/ms190639(v=sql.105).aspx [Missing Index Dynamic Management Views]: http://technet.microsoft.com/library/ms345421.aspx [Performance Considerations for Entity Framework 5]: http://msdn.microsoft.com/data/hh949853 [Code First Data Annotations]: http://msdn.microsoft.com/data/jj591583.aspx [Index Annotations in Entity Framework]:http://msdn.microsoft.com/data/jj591583.aspx#Index [How much does that key cost?]: http://www.sqlskills.com/blogs/kimberly/how-much-does-that-key-cost-plus-sp_helpindex9/ ================================================ FILE: docs/mobile-services-store-scripts-source-control.md ================================================ # Store your mobile service project code in source control >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [.NET backend](mobile-services-dotnet-backend-store-code-source-control.md) - [Javascript backend](mobile-services-store-scripts-source-control.md) This topic shows you how to use the source control provided by Azure Mobile Services to store your server scripts. Scripts and other JavaScript backend code files can be promoted from your local Git repository to your production mobile service. It also shows how to define shared code that can be required by multiple scripts and how to use the package.json file to add Node.js modules to your mobile service. To complete this tutorial, you must have already created a mobile service by completing the [Get started with Mobile Services] tutorial. ## Enable source control in your mobile service To be able to store app data in the new mobile service, you must first create a new table in the associated SQL Database instance. 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, click your mobile service, then click the **Dashboard** tab. 2. (Optional) If you have already set the Mobile Services or Websites source control credentials for your Azure subscription, then you can skip down to step 4. Otherwise, click **Set up source control** under **Quick glance** and click **Yes** to confirm. ![Set up source control](./media/mobile-services-enable-source-control/mobile-setup-source-control.png) 3. Supply a **User name**, **New password**, confirm the password, then click the check button. The Git repository is created in your mobile service. Make a note of the credentials you just supplied; you will use them to access this and other Mobile Services repositories in your subscription. 4. Click the **Configure** tab and notice the **Source control** fields. ![Configure source control](./media/mobile-services-enable-source-control/mobile-source-control-configure.png) The URL of the Git repository is displayed. You will use this URL to clone the repository to your local computer. With source control enabled in your mobile service, you can use Git to clone the repository to your local computer. ## Install Git and create the local repository 1. Install Git on your local computer. The steps required to install Git vary between operating systems. See [Installing Git] for operating system specific distributions and installation guidance. > [AZURE.NOTE] > On some operating systems, both a command-line and GUI version of Git are available. The instructions provided in this article use the command-line version. 2. Open a command-line, such as **GitBash** (Windows) or **Bash** (Unix Shell). On OS X systems you can access the command-line through the **Terminal** application. 3. From the command line, change to the directory where you will store your scripts. For example, `cd SourceControl`. 4. Use the following command to create a local copy of your new Git repository, replacing `` with the URL of the Git repository for your mobile service: git clone 5. When prompted, type in the user name and password that you set when you enabled source control in your mobile service. After successful authentication, you will see a series of responses like this: remote: Counting objects: 8, done. remote: Compressing objects: 100% (4/4), done. remote: Total 8 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (8/8), done. 6. Browse to the directory from which you ran the `git clone` command, and notice the following directory structure: ![4][4] In this case, a new directory is created with the name of the mobile service, which is the local repository for the data service. 7. Open the .\service\table subfolder and notice that it contains a TodoItem.json file, which is a JSON representation of the operation permissions on the TodoItem table. When server scripts have been defined on this table, you will also have one or more files named TodoItem._<operation>_.js that contain the scripts for the given table operation. Scheduler and custom API scripts are maintained in separate folders with those respective names. For more information, see [Source control]. Now that you have created your local repository, you can make changes to server scripts and push the changes back to the mobile service. ## Deploy updated script files to your mobile service 1. Browse to the .\service\table subfolder, and if a file todoitem.insert.js files doesn't already exist, create it now. 2. Open the new file todoitem.insert.js in a text editor and paste in the following code and save your changes: function insert(item, user, request) { request.execute(); console.log(JSON.stringify(item, null, 4)); } This code simply writes the inserted item to the log. If this file already contains code, simply add some valid JavaScript code to this file, such as a call to `console.log()`, then save your changes. 3. In the Git command prompt, type the following command to start tracking the new script file: $ git add . 4. Type the following command to commit changes: $ git commit -m "updated the insert script" 5. Type the following command to upload the changes to the remote repository: $ git push origin master You should see a series of commands that indicates that the commit is deployed to the mobile service. 6. Back in the [Azure classic portal], click the **Data** tab, then click the **TodoItem** table, click **Script**, then select the **Insert** operation. Notice that the displayed insert operation script is the same as the JavaScript code that you just uploaded to the repository. ## Leverage shared code and Node.js modules in your server scripts Mobile Services provides access to the full set of core Node.js modules, which you can use in your code by using the **require** function. Your mobile service can also use Node.js modules that are not part of the core Node.js package, and you can even define your own shared code as Node.js modules. For more information about creating modules, see [Modules] in the Node.js API reference documentation. The recommended way to add Node.js modules to your mobile service is by adding references to the service's package.json file. Next, you will add the [node-uuid] Node.js module to your mobile service by updating the package.json file. When the update is pushed to Azure, the mobile service is restarted and the module is installed. This module is then used to generate a new GUID value for the **uuid** property on inserted items. 2. Navigate to the `.\service` folder of your local Git repository, and open the package.json file in a text editor, and add the following field to the **dependencies** object: "node-uuid": "~1.4.3" >[AZURE.NOTE]This update to the package.json file will cause a restart in your mobile service after the commit is pushed. 4. Now browse to the .\service\table subfolder, open the todoitem.insert.js file and modify it as follows: function insert(item, user, request) { var uuid = require('node-uuid'); item.uuid = uuid.v1(); request.execute(); console.log(item); } This code adds a uuid column to the table, populating it with unique GUID identifiers. 5. As in the previous section, type the following command in the Git command prompt: $ git add . $ git commit -m "added node-uuid module" $ git push origin master This adds the new file, commits your changes, and pushes the new node-uuid module and changes to the todoitem.insert.js script to your mobile service. ## Next steps Now that you have completed this tutorial you know how to store your scripts in source control. Consider learning more about working with server scripts and with custom APIs: + [Work with server scripts in Mobile Services]
Shows how to work with server scripts, job scheduler, and custom APIs. [Enable source control in your mobile service]: #enable-source-control [Install Git and create the local repository]: #clone-repo [Deploy updated script files to your mobile service]: #deploy-scripts [Leverage shared code and Node.js modules in your server scripts]: #use-npm [4]: ./media/mobile-services-store-scripts-source-control/mobile-source-local-repo.png [5]: ./media/mobile-services-store-scripts-source-control/mobile-portal-data-tables.png [6]: ./media/mobile-services-store-scripts-source-control/mobile-insert-script-source-control.png [Git website]: http://git-scm.com [Source control]: http://msdn.microsoft.com/library/windowsazure/c25aaede-c1f0-4004-8b78-113708761643 [Installing Git]: http://git-scm.com/book/en/Getting-Started-Installing-Git [Get started with Mobile Services]: mobile-services-ios-get-started.md [Work with server scripts in Mobile Services]: mobile-services-how-to-use-server-scripts.md [Azure classic portal]: https://manage.windowsazure.com/ [Modules]: http://nodejs.org/api/modules.html [node-uuid]: https://npmjs.org/package/node-uuid ================================================ FILE: docs/mobile-services-using-soft-delete.md ================================================ # Using soft delete in Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   ## Overview Tables created with either the JavaScript or .NET backend can optionally have soft delete enabled. When using soft delete, a new column called *\__deleted* of [SQL bit type] is added to the database. With soft delete enabled, a delete operation does not physically delete rows from the database, but rather sets the value of the deleted column to TRUE. When querying records on a table with soft delete enabled, deleted rows are not returned in the query by default. In order to request these rows, you must pass a query parameter *\__includeDeleted=true* in your [REST Query operation](http://msdn.microsoft.com/library/azure/jj677199.aspx). In the .NET client SDK, you can also use the helper method `IMobileServiceTable.IncludeDeleted()`. Soft delete support for the .NET backend first released with version 1.0.402 of the Microsoft Azure Mobile Services .NET Backend. The latest NuGet packages are available here, [Microsoft Azure Mobile Services .NET Backend](http://go.microsoft.com/fwlink/?LinkId=513165). Some of the potential benefits of using soft delete: * When using the [Offline data Sync for Mobile Services] feature, the client SDK automatically queries for deleted records and removes them from the local database. Without soft delete enabled, you need to write additional code on the backend so that the client SDK knows which records to remove from the local store. Otherwise, the client local store and backend will be inconsistent with regard to these deleted records and the client method `PurgeAsync()` must be called to clear the local store. * Some applications have a business requirement to never physically delete data, or to delete data only after it has been audited. The soft delete feature can be useful in this scenario. * Soft delete can be used to implement an "undelete" feature, so that data deleted by accident can be recovered. However, soft deleted records take up space in the database, so you should consider creating a scheduled job to periodically hard delete the soft deleted records. For an example of this, see [Using soft delete with the .NET backend](#using-with-dotnet) and [Using soft delete with the JavaScript backend](#using-with-javascript). Your client code should also periodically call `PurgeAsync()` so that these hard deleted records do not remain in the device's local data store. ## Enabling soft delete for the .NET backend Soft delete support for the .NET backend first released with version 1.0.402 of the Microsoft Azure Mobile Services .NET Backend. The latest NuGet packages are available here, [Microsoft Azure Mobile Services .NET Backend](http://go.microsoft.com/fwlink/?LinkId=513165). The following steps guide you on how to enable soft delete for a .NET backend mobile service. 1. Open your .NET backend mobile service project in Visual Studio. 2. Right click the .NET backend project and click **Manage NuGet Packages**. 3. In the package manager dialog, click **Nuget.org** under updates and install version 1.0.402 or later of the [Microsoft Azure Mobile Services .NET Backend](http://go.microsoft.com/fwlink/?LinkId=513165) NuGet packages. 3. In Solution Explorer for Visual Studio, expand the **Controllers** node under your .NET backend project and open your controller source for. For example, *TodoItemController.cs*. 4. In the `Initialize()` method of your controller, pass the `enableSoftDelete: true` parameter to the EntityDomainManager constructor. protected override void Initialize(HttpControllerContext controllerContext) { base.Initialize(controllerContext); MobileService1Context context = new MobileService1Context(); DomainManager = new EntityDomainManager(context, Request, Services, enableSoftDelete: true); } ## Enabling soft delete for the JavaScript backend If you are creating a new table for your mobile service, you can enable soft delete on the table creation page. ![][2] To enable soft delete on an existing table in the JavaScript backend: 1. In the [Azure classic portal], click your mobile service. Then click the Data tab. 2. On the data page, click to select the desired table. Then click the **Enable Soft Delete** button in the command bar. If the table already has soft delete enabled, this button will not appear but you will be able to see the *\__deleted* column when clicking the **Browse** or **Columns** tab for the table. ![][0] To disable soft delete for your table, click the **Columns** tab and then click the *\__deleted* column and the **Delete** button. ![][1] ## Using soft delete with the .NET backend The following scheduled job purges soft deleted records that are more than a month old: public class SampleJob : ScheduledJob { private MobileService1Context context; protected override void Initialize(ScheduledJobDescriptor scheduledJobDescriptor, CancellationToken cancellationToken) { base.Initialize(scheduledJobDescriptor, cancellationToken); context = new MobileService1Context(); } public override Task ExecuteAsync() { Services.Log.Info("Purging old records"); var monthAgo = DateTimeOffset.UtcNow.AddDays(-30); var toDelete = context.TodoItems.Where(x => x.Deleted == true && x.UpdatedAt <= monthAgo).ToArray(); context.TodoItems.RemoveRange(toDelete); context.SaveChanges(); return Task.FromResult(true); } } To learn more about schedule jobs with .NET backend Mobile Services, see: [Schedule recurring jobs with JavaScript backend Mobile Services](mobile-services-dotnet-backend-schedule-recurring-tasks.md) ## Using soft delete with the JavaScript backend You use table scripts to add logic around the soft delete feature with JavaScript backend mobile services. To detect an undelete request, use the property "undelete" in your update table script: function update(item, user, request) { if (request.undelete) { /* any undelete specific code */; } } To include deleted records in query result in a script, set the "includeDeleted" parameter to true: tables.getTable('softdelete_scenarios').read({ includeDeleted: true, success: function (results) { request.respond(200, results) } }); To retrieve deleted records via an HTTP request, add the query parameter "__includedeleted=true": http://youservice.azure-mobile.net/tables/todoitem?__includedeleted=true ### Sample scheduled job for purging soft deleted records. This is a sample scheduled job that deletes records that were updated prior to a particular date: function purgedeleted() { mssql.query('DELETE FROM softdelete WHERE __deleted=1', { success: function(results) { console.log(results); }, error: function(err) { console.log("error is: " + err); }}); } To learn more about scheduled jobs with JavaScript backend Mobile Services, see: [Schedule recurring jobs with JavaScript backend Mobile Services](mobile-services-schedule-recurring-tasks.md). [0]: ./media/mobile-services-using-soft-delete/enable-soft-delete-button.png [1]: ./media/mobile-services-using-soft-delete/disable-soft-delete.png [2]: ./media/mobile-services-using-soft-delete/enable-soft-delete-new-table.png [SQL bit type]: http://msdn.microsoft.com/library/ms177603.aspx [Offline data Sync for Mobile Services]: mobile-services-windows-store-dotnet-get-started-offline-data.md [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/mobile-services-windows-phone-get-started-data.md ================================================ # Add Mobile Services to an existing app >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-data.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-data.md) - [(Android | Javascript)](mobile-services-android-get-started-data.md) ## Overview This topic shows you how to use Azure Mobile Services to leverage data in a Windows Phone 8 app. In this tutorial, you will download an app that stores data in memory, create a new mobile service, integrate the mobile service with the app, and then login to the [Azure classic portal] to view changes to data made when running the app. You can also see Nick Harris demonstrate this in the following video: >[AZURE.VIDEO mobile-get-started-with-data-windows-phone] ## Prerequisites + Visual Studio 2012 Express for Windows Phone 8 and the [Windows Phone 8 SDK] running on Windows 8. To complete this tutorial to create a Windows Phone 8.1 app, you must use Visual Studio 2013 Update 2, or a later version. + An Azure account. If you don't have an account, you can create a free trial account in just a couple of minutes. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A756A2826&returnurl=http%3A%2F%2Fazure.microsoft.com%2Farticles%2Fdocumentation%2Fmobile-services-windows-phone-get-started-data%2F). ## Download the GetStartedWithData project This tutorial is built on the [GetStartedWithData app][Developer Code Samples site], which is a Windows Phone Silverlight 8 app project. 1. Download the GetStartedWithData sample app project from the [Developer Code Samples site]. >[AZURE.NOTE]To create a Windows Phone Silverlght 8.1 app, just change the target OS in the downloaded Windows Phone Silverlight 8 app project to Windows Phone 8.1. To create a Windows Phone Store app, download the [Windows Phone Store app version](http://go.microsoft.com/fwlink/p/?LinkId=397372) of the GetStartedWithData sample app project. 2. In Visual Studio, open the downloaded project and examine the MainPage.xaml.cs file. Notice that added **TodoItem** objects are stored in an in-memory **ObservableCollection<TodoItem>**. 3. Press the **F5** key to rebuild the project and start the app. 4. In the app, type some text in the text box, then click the **Save** button. ![][0] Notice that the saved text is displayed in the list below. ## Create a new mobile service in the Azure classic portal Next, you will create a new mobile service to replace the in-memory list for data storage. Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). 2. At the bottom of the navigation pane, click **+NEW**. ![plus-new](./media/mobile-services-create-new-service-data/plus-new.png) 3. Expand **Compute** and **Mobile Service**, then click **Create**. ![mobile-create](./media/mobile-services-create-new-service-data/mobile-create.png) This displays the **New Mobile Service** dialog. 4. In the **Create a mobile service** page, select **Create a free 20 MB SQL Database**, then type a subdomain name for the new mobile service in the **URL** textbox and wait for name verification. Once name verification completes, click the right arrow button to go to the next page. ![mobile-create-page1](./media/mobile-services-create-new-service-data/mobile-create-page1.png) This displays the **Specify database settings** page. > [AZURE.NOTE] As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 5. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![mobile-create-page2](./media/mobile-services-create-new-service-data/mobile-create-page2.png) > [AZURE.NOTE] When the password that you supply does not meet the minimum requirements or when there is a mismatch, a warning is displayed. > > We recommend that you make a note of the administrator login name and password that you specify; you will need this information to reuse the SQL Database instance or the server in the future. You have now created a new mobile service that can be used by your mobile apps. Next, you will add a new table in which to store app data. This table will be used by the app in place of the in-memory collection. ## Add a new table to the mobile service To be able to store app data in the new mobile service, you must first create a new table in the associated SQL Database instance. 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click the mobile service that you just created. 2. Click the **Data** tab, then click **+Create**. This displays the **Create new table** dialog. 3. In **Table name** type _TodoItem_, then click the check button. This creates a new storage table **TodoItem** with the default permissions set. This means that anyone with the application key, which is distributed with your app, can access and change data in the table. >[AZURE.NOTE] The same table name is used in Mobile Services quickstart. However, each table is created in a schema that is specific to a given mobile service. This is to prevent data collisions when multiple mobile services use the same database. 4. Click the new **TodoItem** table and verify that there are no data rows. 5. Click the **Columns** tab. Verify that the following default columns are automatically created for you:
Column Name Type Index
id string Indexed
__createdAt date Indexed
__updatedAt date -
__version timestamp (MSSQL) -
This is the minimum requirement for a table in Mobile Services. > [AZURE.NOTE] When dynamic schema is enabled on your mobile service, new columns are created automatically when JSON objects are sent to the mobile service by an insert or update operation. You are now ready to use the new mobile service as data storage for the app. ## Update the app to use the mobile service for data access Now that your mobile service is ready, you can update the app to store items in Mobile Services instead of the local collection. 1. In **Solution Explorer** in Visual Studio, right-click the project name, and then select **Manage NuGet Packages**. 2. In the left pane, select the **Online** category, search for `WindowsAzure.MobileServices`, click **Install** on the **Azure Mobile Services** package, then accept the license agreement. ![][7] This adds the Mobile Services client library to the project. 3. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service you just created. 4. Click the **Dashboard** tab and make a note of the **Site URL**, then click **Manage keys** and make a note of the **Application key**. ![][8] You will need these values when accessing the mobile service from your app code. 5. In Visual Studio, open the file App.xaml.cs and add or uncomment the following `using` statement: using Microsoft.WindowsAzure.MobileServices; 6. In this same file, uncomment the following code that defines the **MobileService** variable, and supply the URL and application key from the mobile service in the **MobileServiceClient** constructor, in that order. //public static MobileServiceClient MobileService = new MobileServiceClient( // "AppUrl", // "AppKey" //); This creates a new instance of **MobileServiceClient** that is used to access your mobile service. 6. In the file MainPage.cs, add or uncomment the following `using` statements: using Microsoft.WindowsAzure.MobileServices; using Newtonsoft.Json; 7. In this DataModel folder, replace the **TodoItem** class definition with the following code: public class TodoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } } 7. Comment the line that defines the existing **items** collection, then uncomment the following lines: private MobileServiceCollection items; private IMobileServiceTable todoTable = App.MobileService.GetTable(); This code creates a mobile services-aware binding collection (**items**) and a proxy class for the SQL Database table **TodoItem** (**todoTable**). 7. In the **InsertTodoItem** method, remove the line of code that sets the **TodoItem**.**Id** property, add the **async** modifier to the method, and uncomment the following line of code: await todoTable.InsertAsync(todoItem); This code inserts a new item into the table. 8. In the **RefreshTodoItems** method, add the **async** modifier to the method, then uncomment the following line of code: items = await todoTable.ToCollectionAsync(); This sets the binding to the collection of items in the todoTable, which contains all TodoItem objects returned from the mobile service. 9. In the **UpdateCheckedTodoItem** method, add the **async** modifier to the method, and uncomment the following line of code: await todoTable.UpdateAsync(item); This sends an item update to the mobile service. Now that the app has been updated to use Mobile Services for backend storage, it's time to test the app against Mobile Services. ## Test the app against your new mobile service 1. In Visual Studio, press the F5 key to run the app. 2. As before, type text in the textbox, and then click **Save**. This sends a new item as an insert to the mobile service. 3. In the [Azure classic portal], click **Mobile Services**, and then click your mobile service. 4. Click the **Data** tab, then click **Browse**. ![][9] Notice that the **TodoItem** table now contains data, with id values generated by Mobile Services, and that columns have been automatically added to the table to match the TodoItem class in the app. This concludes the tutorial. ## Next steps This tutorial demonstrated the basics of enabling a Windows Phone 8 app to work with data in Mobile Services. Next, consider reading up on one of these other topics: * [Add authentication to your app](mobile-services-windows-phone-get-started-users.md)
Learn how to authenticate users of your app. * [Add push notifications to your app](mobile-services-javascript-backend-windows-phone-get-started-push.md)
Learn how to send a very basic push notification to your app with Mobile Services. * [Mobile Services C# How-to Conceptual Reference](mobile-services-dotnet-how-to-use-client-library.md)
Learn more about how to use Mobile Services with .NET. [Download the Windows Phone 8 app project]: #download-app [Create the mobile service]: #create-service [Add a data table for storage]: #add-table [Update the app to use Mobile Services]: #update-app [Test the app against Mobile Services]: #test-app [Next Steps]:#next-steps [0]: ./media/mobile-services-windows-phone-get-started-data/mobile-quickstart-startup-wp8.png [7]: ./media/mobile-services-windows-phone-get-started-data/mobile-add-nuget-package-wp.png [8]: ./media/mobile-services-windows-phone-get-started-data/mobile-dashboard-tab.png [9]: ./media/mobile-services-windows-phone-get-started-data/mobile-todoitem-data-browse.png [Azure classic portal]: https://manage.windowsazure.com/ [Windows Phone 8 SDK]: http://go.microsoft.com/fwlink/p/?LinkID=268374 [Mobile Services SDK]: http://go.microsoft.com/fwlink/p/?LinkID=268375 [Developer Code Samples site]: http://go.microsoft.com/fwlink/p/?LinkId=271146 ================================================ FILE: docs/mobile-services-windows-phone-get-started-users.md ================================================ # Add authentication to your Mobile Services app >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows 8.x Store C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started-users.md) - [(Windows 8.x Store C# | Javascript)](mobile-services-windows-store-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md) ## Overview This topic shows you how to authenticate users in Azure Mobile Services from your app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial is also demonstrated by Nick Harris in the following video: > [AZURE.VIDEO mobile-authorize-users-in-scripts-windows-phone] This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Add Mobile Services to an existing app]. >[AZURE.NOTE]This tutorial demonstrates the authentication flow managed by Mobile Services using a variety of identity providers. This method is easy to configure and supports multiple providers. By using client-managed authentication, your app has access to additional user data maintained by the identity provider. You can get the same user data in your mobile service by by calling the **user.getIdentities()** function in server scripts. For more information, see [this post](http://go.microsoft.com/fwlink/p/?LinkId=506605). ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario.   3. In Visual Studio, open the project that you created when you completed the tutorial [Add Mobile Services to an existing app](mobile-services-windows-phone-get-started-data.md).   4. Press the F5 key to run this quickstart-based app; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the *TodoItem* table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app 1. Open the project file mainpage.xaml.cs and add the following code snippet to the MainPage class: private MobileServiceUser user; private async Task Authenticate() { while (user == null) { string message; try { user = await App.MobileServiceDotNetClient.LoginAsync(MobileServiceAuthenticationProvider.Twitter); message = string.Format("You are now logged in - {0}", user.UserId); } catch (InvalidOperationException) { message = "You must log in. Login Required"; } var dialog = new MessageDialog(message); await dialog.ShowAsync(); } } This creates a member variable for storing the current user and a method to handle the authentication process. The user is authenticated by using a Twitter login. >[AZURE.NOTE]If you are using an identity provider other than Twitter, change the value of MobileServiceAuthenticationProvider above to the value for your provider.

2. Delete or comment-out the existing **OnNavigatedTo** method override and replace it with the following method that handles the **Loaded** event for the page. async void MainPage_Loaded(object sender, RoutedEventArgs e) { await Authenticate(); RefreshTodoItems(); } This method calls the new **Authenticate** method. 3. Replace the MainPage constructor with the following code: // Constructor public MainPage() { InitializeComponent(); this.Loaded += MainPage_Loaded; } This constructor also registers the handler for the Loaded event. 4. Press the F5 key to run the app and sign into the app with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query Mobile Services and make updates to data. ## Store the authorization tokens on the client The previous example showed a standard sign-in, which requires the client to contact both the identity provider and the mobile service every time that the app starts. Not only is this method inefficient, you can run into usage-relates issues should many customers try to start you app at the same time. A better approach is to cache the authorization token returned by Mobile Services and try to use this first before using a provider-based sign-in. >[AZURE.NOTE]You can cache the token issued by Mobile Services regardless of whether you are using client-managed or service-managed authentication. This tutorial uses service-managed authentication. 1. In the MainPage.xaml.cs project file, add the following **using** statements: using System.IO.IsolatedStorage; using System.Security.Cryptography; 2. Replace the **AuthenticateAsync** method with the following code: private async System.Threading.Tasks.Task AuthenticateAsync() { string message; // This sample uses the Facebook provider. var provider = "Facebook"; // Provide some additional app-specific security for the encryption. byte [] entropy = { 1, 8, 3, 6, 5 }; // Authorization credential. MobileServiceUser user = null; // Isolated storage for the app. IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings; while (user == null) { // Try to get an existing encrypted credential from isolated storage. if (settings.Contains(provider)) { // Get the encrypted byte array, decrypt and deserialize the user. var encryptedUser = settings[provider] as byte[]; var userBytes = ProtectedData.Unprotect(encryptedUser, entropy); user = JsonConvert.DeserializeObject( System.Text.Encoding.Unicode.GetString(userBytes, 0, userBytes.Length)); } if (user != null) { // Set the user from the stored credentials. App.MobileService.CurrentUser = user; try { // Try to return an item now to determine if the cached credential has expired. await App.MobileService.GetTable().Take(1).ToListAsync(); } catch (MobileServiceInvalidOperationException ex) { if (ex.Response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { // Remove the credential with the expired token. settings.Remove(provider); user = null; continue; } } } else { try { // Login with the identity provider. user = await App.MobileService .LoginAsync(provider); // Serialize the user into an array of bytes and encrypt with DPAPI. var userBytes = System.Text.Encoding.Unicode .GetBytes(JsonConvert.SerializeObject(user)); byte[] encryptedUser = ProtectedData.Protect(userBytes, entropy); // Store the encrypted user credentials in local settings. settings.Add(provider, encryptedUser); settings.Save(); } catch (MobileServiceInvalidOperationException ex) { message = "You must log in. Login Required"; } } message = string.Format("You are now logged in - {0}", user.UserId); MessageBox.Show(message); } } In this version of **AuthenticateAsync**, the app tries to use credentials stored encrypted in local storage to access the mobile service. A simple query is sent to verify that the stored token is not expired. When a 401 is returned, a regular provider-based sign-in is attempted. A regular sign-in is also performed when there is no stored credential. >[AZURE.NOTE]This app tests for expired tokens during login, but token expiration can occur after authentication when the app is in use. For a solution to handling authorization errors related to expiring tokens, see the post [Caching and handling expired tokens in Azure Mobile Services managed SDK](http://blogs.msdn.com/b/carlosfigueira/archive/2014/03/13/caching-and-handling-expired-tokens-in-azure-mobile-services-managed-sdk.aspx). 3. Restart the app twice. Notice that on the first start-up, sign-in with the provider is again required. However, on the second restart the cached credentials are used and sign-in is bypassed. ## Next steps In the next tutorial, [Service-side authorization of Mobile Services users](mobile-services-javascript-backend-service-side-authorization.md), you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [1]: ./media/mobile-services-wp8-get-started-users/mobile-services-selection.png [2]: ./media/mobile-services-wp8-get-started-users/mobile-service-uri.png [3]: ./media/mobile-services-wp8-get-started-users/mobile-identity-tab.png [4]: ./media/mobile-services-wp8-get-started-users/mobile-portal-data-tables.png [5]: ./media/mobile-services-wp8-get-started-users/mobile-portal-change-table-perms.png [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [Add Mobile Services to an existing app]: mobile-services-windows-phone-get-started-data.md [Authorize users with scripts]: mobile-services-windows-phone-authorize-users-in-scripts.md ================================================ FILE: docs/mobile-services-windows-store-dotnet-adal-sso-authentication.md ================================================ # Authenticate your app with Active Directory Authentication Library Single Sign-On >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-adal-sso-authentication.md) - [(Windows 8.x Store C# | .NET)](mobile-services-windows-store-dotnet-adal-sso-authentication.md) ## Overview In this tutorial, you add authentication to the quickstart project using the Active Directory Authentication Library to support [client-directed login operations](http://msdn.microsoft.com/library/azure/jj710106.aspx) with Azure Active Directory. To support [service-directed login operations](http://msdn.microsoft.com/library/azure/dn283952.aspx) with Azure Active Directory, start with the [Add authentication to your Mobile Services app](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) tutorial. To be able to authenticate users, you must register your application with the Azure Active Directory (AAD). This is done in two steps. First, you must register your mobile service and expose permissions on it. Second, you must register your Windows Store app and grant it access to those permissions >[AZURE.NOTE] This tutorial is intended to help you better understand how Mobile Services enables you to do single sign-on Azure Active Directory authentication for Windows Store apps using a [client-directed login operation](http://msdn.microsoft.com/library/azure/jj710106.aspx). If this is your first experience with Mobile Services, complete the tutorial [Get started with Mobile Services]. ## Prerequisites This tutorial requires the following: * Visual Studio 2013 running on Windows 8.1. * Completion of the [Get started with Mobile Services] tutorial. * Microsoft Azure Mobile Services SDK NuGet package * Active Directory Authentication Library NuGet package ## Register your mobile service with the Azure Active Directory In this section you will register your mobile service with the Azure Active Directory and configure permissions to allow single sign-on impersonation. 1. Register your application with your Azure Active Directory by following the [How to Register with the Azure Active Directory] topic. 2. In the [Azure classic portal](https://manage.windowsazure.com/), go back to the Azure Active Directory extension and click on your active directory 3. Click the **Applications** tab and then click your application. 4. Click **Manage Manifest**. Then click **Download Manifest** and save the application manifest to a local directory. ![](./media/mobile-services-dotnet-adal-register-service/mobile-services-aad-app-manage-manifest.png) 5. Open the application manifest file with Visual Studio. At the top of the file find the app permissions line that looks as follows: "oauth2Permissions": [], Replace that line with the following app permissions and save the file. "oauth2Permissions": [ { "adminConsentDescription": "Allow the application access to the mobile service", "adminConsentDisplayName": "Have full access to the mobile service", "id": "b69ee3c9-c40d-4f2a-ac80-961cd1534e40", "isEnabled": true, "origin": "Application", "type": "User", "userConsentDescription": "Allow the application full access to the mobile service on your behalf", "userConsentDisplayName": "Have full access to the mobile service", "value": "user_impersonation" } ], 6. In the [Azure classic portal](https://manage.windowsazure.com/), click **Manage Manifest** for the application again and click **Upload Manifest**. Browse to the location of the application manifest that you just updated and upload the manifest. [How to Register with the Azure Active Directory]: ./ mobile-services-how-to-register-active-directory-authentication.md ## Register your app with the Azure Active Directory To register the app with Azure Active Directory, you must associate it to the Windows Store and have a package security identifier (SID) for the app. The package SID gets registered with the native application settings in the Azure Active Directory. ### Associate the app with a new store app name 1. In Visual Studio, right click the client app project and click **Store** and **Associate App with the Store** ![][1] 2. Sign into your Dev Center account. 3. Enter the app name you want to reserve for the app and click **Reserve**. ![][2] 4. Select the new app name and click **Next**. 5. Click **Associate** to associate the app with the store name. ### Retrieve the package SID for your app. Now you need to retrieve your package SID which will be configured with the native app settings. 1. Log into your [Windows Dev Center Dashboard] and click **Edit** on the app. ![][3] 2. Then click **App management** > **App identity** and Copy your package SID from the page. ![][4] ### Create the native app registration 1. Navigate to **Active Directory** in the [classic portal], then click your directory. ![][7] 2. Click the **Applications** tab at the top, then click to **ADD** an app. ![][8] 3. Click **Add an application my organization is developing**. 4. In the Add Application Wizard, enter a **Name** for your application and click the **Native Client Application** type. Then click to continue. ![][9] 5. In the **Redirect URI** box, paste the App package SID you copied earlier then click to complete the native app registration. ![][10] 6. Click the **Configure** tab for the native application and copy the **Client ID**. You will need this later. ![][11] 7. Scroll the page down to the **permissions to other applications** section and grant full access to the mobile service application that you registered earlier. Then click **Save** ![][12] Your mobile service is now configured in AAD to receive single sign-on logins from your app. ## Configure the mobile service to require authentication By default, all requests to mobile service resources are restricted to clients that present the application key, which does not strictly secure access to resources. To secure your resources, you must restrict access to only authenticated clients. 1. In Visual Studio, open your mobile service project, expand the Controllers folder, and open **TodoItemController.cs**. The **TodoItemController** class implements data access for the TodoItem table. Add the following `using` statement: using Microsoft.WindowsAzure.Mobile.Service.Security; 2. Apply the following _AuthorizeLevel_ attribute to the **TodoItemController** class. [AuthorizeLevel(AuthorizationLevel.User)] This makes sure that all operations against the _TodoItem_ table require an authenticated user. You can also apply the *AuthorizeLevel* attribute at the method level. 3. (Optional) If you wish to debug authentication locally, expand the `App_Start` folder, open **WebApiConfig.cs**, and add the following code to the **Register** method. config.SetIsHosted(true); This tells the local mobile service project to run as if it is being hosted in Azure, including honoring the *AuthorizeLevel* settings. Without this setting, all HTTP requests to localhost are permitted without authentication despite the *AuthorizeLevel* setting. When you enable self-hosted mode, you also need to set a value for the local application key. 4. (Optional) In the web.config project file, set a string value for the `MS_ApplicationKey` app setting. This is the password that you use (with no username) to test the API help pages when you run the service locally. This string value is not used by the live site in Azure, and you do not need to use the actual application key; any valid string value will work. 4. Republish your project. ## Add authentication code to the client app 1. Open your Windows store client app project in Visual Studio. 1. In the Solution Explorer window of Visual Studio, right click the project and click **Manage NuGet Packages**. 2. In the NuGet Package manager, click **Online**. Enter **Microsoft.IdentityModel.Clients.ActiveDirectory** as a search term. Then click **Install** to install the Active Directory Authentication Library Nuget package. ![](./media/mobile-services-dotnet-adal-install-nuget/mobile-services-adal-nuget-package.png) 4. In the Solution Explorer window of Visual Studio, open the MainPage.cs file and add the following using statements. using Windows.UI.Popups; using Microsoft.IdentityModel.Clients.ActiveDirectory; using Newtonsoft.Json.Linq; 5. Add the following code to the MainPage class which declares the `AuthenticateAsync` method. private MobileServiceUser user; private async Task AuthenticateAsync() { string authority = ""; string resourceURI = ""; string clientID = ""; while (user == null) { string message; try { AuthenticationContext ac = new AuthenticationContext(authority); AuthenticationResult ar = await ac.AcquireTokenAsync(resourceURI, clientID, (Uri) null); JObject payload = new JObject(); payload["access_token"] = ar.AccessToken; user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload); message = string.Format("You are now logged in - {0}", user.UserId); } catch (InvalidOperationException) { message = "You must log in. Login Required"; } var dialog = new MessageDialog(message); dialog.Commands.Add(new UICommand("OK")); await dialog.ShowAsync(); } } 6. In the code for the `AuthenticateAsync` method above, replace **INSERT-AUTHORITY-HERE** with the name of the tenant in which you provisioned your application, the format should be https://login.windows.net/tenant-name.onmicrosoft.com. This value can be copied out of the Domain tab in your Azure Active Directory in the [Azure classic portal]. 7. In the code for the `AuthenticateAsync` method above, replace **INSERT-RESOURCE-URI-HERE** with the **App ID URI** for your mobile service. If you followed the [How to Register with the Azure Active Directory] topic your App ID URI should be similar to https://todolist.azure-mobile.net/login/aad. 8. In the code for the `AuthenticateAsync` method above, replace **INSERT-CLIENT-ID-HERE** with the client ID you copied from the native client application. 9. In the Solution Explorer window for Visual Studio, open the Package.appxmanifest file in the client project. Click the **Capabilities** tab and enable **Enterprise Application** and **Private Networks (Client & Server)**. Save the file. ![][14] 10. In the MainPage.cs file, update the `OnNavigatedTo` event handler to call the `AuthenticateAsync` method as follows. protected override async void OnNavigatedTo(NavigationEventArgs e) { await AuthenticateAsync(); await RefreshTodoItems(); } ## Test the client using authentication 1. In Visual Studio,run the client app. 2. You will receive a prompt to login against your Azure Active Directory. 3. The app authenticates and returns the todo items. ![][15] [0]: ./media/mobile-services-windows-store-dotnet-adal-sso-authenticate/mobile-services-aad-app-manage-manifest.png [1]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-vs-associate-app.png [2]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-vs-reserve-store-appname.png [3]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-store-app-edit.png [4]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-store-app-services.png [5]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-live-services-site.png [6]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-store-app-package-sid.png [7]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-select-aad.png [8]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-aad-applications-tab.png [9]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-native-selection.png [10]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-native-sid-redirect-uri.png [11]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-native-client-id.png [12]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-native-add-permissions.png [14]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-package-appxmanifest.png [15]: ./media/mobile-services-windows-store-dotnet-adal-sso-authentication/mobile-services-app-run.png [How to Register with the Azure Active Directory]: mobile-services-how-to-register-active-directory-authentication.md [Azure classic portal]: https://manage.windowsazure.com/ [classic portal]: https://manage.windowsazure.com/ [Get started with Mobile Services]: mobile-services-dotnet-backend-windows-store-dotnet-get-started.md [Windows Dev Center Dashboard]: http://go.microsoft.com/fwlink/p/?LinkID=266734 ================================================ FILE: docs/mobile-services-windows-store-dotnet-get-started-offline-data.md ================================================ # Using offline data sync in Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR] - [Android)](mobile-services-android-get-started-offline-data.md) - [iOS](mobile-services-ios-get-started-offline-data.md) - [Windows](mobile-services-windows-store-dotnet-get-started-offline-data.md) - [Xamarin.Android](mobile-services-xamarin-android-get-started-offline-data.md) - [Xamarin.iOS](mobile-services-xamarin-ios-get-started-offline-data.md) This tutorial shows you how to add offline support to a Windows Universal Store app using Azure Mobile Services. Offline support will allow you to interact with a local database when your app is in an offline scenario. Once your app is online with the backend database, you sync your local changes using the offline features. If you prefer to watch a video, the clip to the right follows the same steps as this tutorial. > [AZURE.VIDEO build-offline-apps-with-mobile-services] In this tutorial, you update the Universal app project from the [Get started with Mobile Services] tutorial to support the offline features of Azure Mobile Services. Then you will add data in a disconnected offline scenario, sync those items to the online database, and then log in to the [Azure classic portal] to view changes to data made when running the app. >[AZURE.NOTE] This tutorial is intended to help you better understand how Mobile Services enables you to use Azure to store and retrieve data in a Windows Store app. If this is your first experience with Mobile Services, you should complete the tutorial [Get started with Mobile Services] first. ## Prerequisites This tutorial requires the following: * Visual Studio 2013 running on Windows 8.1. * Completion of the [Get started with Mobile Services]. * [Azure Mobile Services SDK version 1.3.0 (or later)][Mobile Services SDK Nuget] * [Azure Mobile Services SQLite Store version 1.0.0 (or later)][SQLite store nuget] * [SQLite for Windows 8.1](http://www.sqlite.org/download.html) * An Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=AE564AB28). ## Update the app to support offline features Azure Mobile Services offline features allow you to interact with a local database when you are in an offline scenario with your Mobile Service. To use these features in your app, you initialize a `MobileServiceClient.SyncContext` to a local store. Then reference your table through the `IMobileServiceSyncTable` interface. In this tutorial we use SQLite for the local store. >[AZURE.NOTE] You can skip this section and just get the example project that already has offline support from the GitHub samples repository for Mobile Services. The sample project with offline support enabled is located here, [TodoList Offline Sample]. 1. Install the SQLite runtime for Windows 8.1 and Windows Phone 8.1. * **Windows 8.1 Runtime:** Install [SQLite for Windows 8.1]. * **Windows Phone 8.1:** Install [SQLite for Windows Phone 8.1]. >[AZURE.NOTE] If you are using Internet Explorer, clicking the link to install SQLite may prompt you to download the .vsix as a .zip file. Save the file to a location on your hard drive with the .vsix extension instead of .zip. The double click the .vsix file in Windows Explorer to run the installation. 2. In Visual Studio open the project that you completed in the [Get started with Mobile Services] tutorial. Install the **WindowsAzure.MobileServices.SQLiteStore** NuGet package for the Windows 8.1 runtime and Windows Phone 8.1 projects. * **Windows 8.1:** In Solution Explorer, right click the Windows 8.1 project and click **Manage Nuget Packages** to run NuGet Package Manager. Search for **SQLiteStore** to install the `WindowsAzure.MobileServices.SQLiteStore` package. * **Windows Phone 8.1:** Right click the Windows Phone 8.1 project and click **Manage Nuget Packages** to run NuGet Package Manager. Search for **SQLiteStore** to install the `WindowsAzure.MobileServices.SQLiteStore` package. >[AZURE.NOTE] If the installation creates a reference to an older version of SQLite, you can just delete that duplicate reference. ![][2] 2. In Solution Explorer, right click **References** for the Windows 8.1 Runtime and Windows Phone 8.1 platform projects and ensure there is a reference to SQLite, which is located in the **Extensions** section. ![][1]
**Windows 8.1 Runtime** ![][11]
**Windows Phone 8.1** 3. The SQLite Runtime requires you to change the processor architecture of the project being built to **x86**, **x64**, or **ARM**. **Any CPU** is not supported. In Solution Explorer, click the Solution at the top, then change the processor architecture drop down box to one of the supported settings that you want to test. ![][13] 5. In Solution Explorer, in the shared project, open the MainPage.cs file. Uncomment the following using statements at the top of the file: using Microsoft.WindowsAzure.MobileServices.SQLiteStore; // offline sync using Microsoft.WindowsAzure.MobileServices.Sync; // offline sync 6. In MainPage.cs, comment the definition of `todoTable` and uncomment the one on the following line that calls `MobileServicesClient.GetSyncTable()`: //private IMobileServiceTable todoTable = App.MobileService.GetTable(); private IMobileServiceSyncTable todoTable = App.MobileService.GetSyncTable(); // offline sync 7. In MainPage.cs, in the region marked `Offline sync`, uncomment the methods `InitLocalStoreAsync` and `SyncAsync`. The method `InitLocalStoreAsync` initializes the client sync context with a SQLite store. private async Task InitLocalStoreAsync() { if (!App.MobileService.SyncContext.IsInitialized) { var store = new MobileServiceSQLiteStore("localstore.db"); store.DefineTable(); await App.MobileService.SyncContext.InitializeAsync(store); } await SyncAsync(); } private async Task SyncAsync() { await App.MobileService.SyncContext.PushAsync(); await todoTable.PullAsync("todoItems", todoTable.CreateQuery()); } 8. In the `OnNavigatedTo` event handler, uncomment the call to `InitLocalStoreAsync`: protected override async void OnNavigatedTo(NavigationEventArgs e) { await InitLocalStoreAsync(); // offline sync await RefreshTodoItems(); } 9. Uncommment the 3 calls to `SyncAsync` in the methods `InsertTodoItem`, `UpdateCheckedTodoItem`, and `ButtonRefresh_Click`: private async Task InsertTodoItem(TodoItem todoItem) { await todoTable.InsertAsync(todoItem); items.Add(todoItem); await SyncAsync(); // offline sync } private async Task UpdateCheckedTodoItem(TodoItem item) { await todoTable.UpdateAsync(item); items.Remove(item); ListItems.Focus(Windows.UI.Xaml.FocusState.Unfocused); await SyncAsync(); // offline sync } private async void ButtonRefresh_Click(object sender, RoutedEventArgs e) { ButtonRefresh.IsEnabled = false; await SyncAsync(); // offline sync await RefreshTodoItems(); ButtonRefresh.IsEnabled = true; } 10. Add exception handlers in the `SyncAsync` method: private async Task SyncAsync() { String errorString = null; try { await App.MobileService.SyncContext.PushAsync(); await todoTable.PullAsync("todoItems", todoTable.CreateQuery()); // first param is query ID, used for incremental sync } catch (MobileServicePushFailedException ex) { errorString = "Push failed because of sync errors: " + ex.PushResult.Errors.Count + " errors, message: " + ex.Message; } catch (Exception ex) { errorString = "Pull failed: " + ex.Message + "\n\nIf you are still in an offline scenario, " + "you can try your Pull again when connected with your Mobile Serice."; } if (errorString != null) { MessageDialog d = new MessageDialog(errorString); await d.ShowAsync(); } } In this example, we retrieve all records in the remote `todoTable`, but it is also possible to filter records by passing a query. The first parameter to `PullAsync` is a query ID that is used for incremental sync, which uses the `UpdatedAt` timestamp to get only records modified since the last sync. The query ID should be a descriptive string that is unique for each logical query in your app. To opt-out of incremental sync, pass `null` as the query ID. This will retrieve all records on each pull operation, which is potentially inefficient. >[AZURE.NOTE] * To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. Note that the `MobileServicePushFailedException` can occur for both a push and a pull operation. It can occur for a pull because the pull operation internally executes a push to make sure all tables along with any relationships are consistent. The next tutorial, [Handling conflicts with offline support for Mobile Services], shows how to handle these sync related exceptions. 11. In Visual Studio, press the **F5** key to rebuild and run the app. The app will behave the same as it did before the offline sync changes, because it does a sync operation on the insert, update, and refresh operations. ## Update the sync behavior of the app In this section, you will modify the app so that it does not sync on the insert and update operations, but only when the **Refresh** button is pressed. Then, you will break the app connection with the mobile service to simulate an offline scenario. When you add data items, they will be held in the local store, but not synced to the mobile service. 1. Open MainPage.cs in the shared project. Edit the methods `InsertTodoItem` and `UpdateCheckedTodoItem` to comment out the calls to `SyncAsync`. 2. Edit App.xaml.cs in the shared project. Comment out the initialization of the **MobileServiceClient** and add the following lines, which use an invalid mobile service URL: public static MobileServiceClient MobileService = new MobileServiceClient( "https://your-mobile-service.azure-mobile.xxx/", "AppKey" ); 3. In `InitLocalStoreAsync()`, comment out the call to `SyncAsync()`, so that the app does not perform a sync on launch. 4. Press **F5** to build and run the app. Enter some new todo items and click **Save** for each one. The new todo items exist only in the local store until they can be pushed to the mobile service. The client app behaves as if its connected to the mobile service supporting all create, read, update, delete (CRUD) operations. 5. Close the app and restart it to verify that the new items you created are persisted to the local store. ## Update the app to reconnect your mobile service In this section you reconnect the app to the mobile service. This simulates the app moving from an offline state to an online state with the mobile service. When you press the Refresh button, data will be synced to your mobile service. 1. Open App.xaml.cs in the shared project. Uncomment your previous initialization of `MobileServiceClient` to add back the correct mobile service URL and app key. 2. Press the **F5** key to rebuild and run the app. Notice that the data looks the same as the offline scenario even though the app is now connected to the mobile service. This is because this app always works with the `IMobileServiceSyncTable` that is pointed to the local store. 3. Log into the [Azure classic portal] and look at the database for your mobile service. If your service uses the JavaScript backend for mobile services, you can browse the data from the **Data** tab of the mobile service. If you are using the .NET backend for your mobile service, in Visual Studio go to **Server Explorer** -> **Azure** -> **SQL Databases**. Right click your database and select **Open in SQL Server Object Explorer**. Notice the data has not been synchronized between the database and the local store. ![][6] 4. In the app, press the **Refresh** button. This causes the app to call `PushAsync` and `PullAsync`. This push operation sends the local store items to the mobile service, then retrieves new data from the mobile service. A push operation is executed off the `MobileServiceClient.SyncContext` instead of the `IMobileServicesSyncTable` and pushes changes on all tables associated with that sync context. This is to cover scenarios where there are relationships between tables. ![][7] 5. In the app, click the check box beside a few items to complete them in the local store. ![][8] 6. Push the **Refresh** button again, which causes `SyncAsync` to be called. `SyncAsync` calls both push and pull, but in this case we could have removed the call to `PushAsync`. This is because a **pull always does a push first**. This is to ensure all tables in the local store along with relationships remain consistent. ![][10] ## Summary In order to support the offline features of mobile services, we used the `IMobileServiceSyncTable` interface and initialized `MobileServiceClient.SyncContext` with a local store. In this case the local store was a SQLite database. The normal CRUD operations for mobile services work as if the app is still connected but, all the operations occur against the local store. When we wanted to synchronize the local store with the server, we used the `IMobileServiceSyncTable.PullAsync` and `MobileServiceClient.SyncContext.PushAsync` methods. * To push changes to the server, we called `IMobileServiceSyncContext.PushAsync()`. This method is a member of `IMobileServicesSyncContext` instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server. * To pull data from a table on the server to the app, we called `IMobileServiceSyncTable.PullAsync`. A pull always issues a push first. This is to ensure all tables in the local store along with relationships remain consistent. There are also overloads of `PullAsync()` that allow a query to be specified in order to filter the data that is stored on the client. If a query is not passed, `PullAsync()` will pull all rows in the corresponding table (or query). You can pass the query to filter only the changes your app needs to sync with. * To enable incremental sync, pass a query ID to `PullAsync()`. The query ID is used to store the last updated timestamp from the results of the last pull operation. The query ID should be a descriptive string that is unique for each logical query in your app. If the query has a parameter, then the same parameter value has to be part of the query ID. For instance, if you are filtering on userid, it needs to be part of the query ID: await PullAsync("todoItems" + userid, syncTable.Where(u => u.UserId = userid)); If you want to opt out of incremental sync, pass `null` as the query ID. In this case, all records will be retrieved on every call to `PullAsync`, which is potentially inefficient. * To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. ## Next steps * [Handling conflicts with offline support for Mobile Services] * [Using Soft Delete in Mobile Services][Soft Delete] [Update the app to support offline features]: #enable-offline-app [Update the sync behavior of the app]: #update-sync [Update the app to reconnect your mobile service]: #update-online-app [Next Steps]:#next-steps [1]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-services-add-reference-sqlite-dialog.png [2]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-services-sqlitestore-nuget.png [6]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-data-browse.png [7]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-data-browse2.png [8]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-services-online-app-run2.png [10]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-data-browse3.png [11]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/mobile-services-add-wp81-reference-sqlite-dialog.png [12]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/new-synchandler-class.png [13]: ./media/mobile-services-windows-store-dotnet-get-started-offline-data/cpu-architecture.png [Handling conflicts with offline support for Mobile Services]: mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md [TodoList Offline Sample]: http://go.microsoft.com/fwlink/?LinkId=394777 [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started/#create-new-service [Getting Started]: mobile-services-dotnet-backend-windows-phone-get-started.md [Get started with Mobile Services]: mobile-services-windows-store-get-started.md [SQLite for Windows 8.1]: http://go.microsoft.com/fwlink/?LinkId=394776 [SQLite for Windows Phone 8.1]: http://go.microsoft.com/fwlink/?LinkId=397953 [Soft Delete]: mobile-services-using-soft-delete.md [Mobile Services SDK Nuget]: http://www.nuget.org/packages/WindowsAzure.MobileServices/1.3.0 [SQLite store nuget]: http://www.nuget.org/packages/WindowsAzure.MobileServices.SQLiteStore/1.0.0 [Azure classic portal]: https://manage.windowsazure.com ================================================ FILE: docs/mobile-services-windows-store-dotnet-handle-database-conflicts.md ================================================ # Handling database write conflicts >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   ## Overview This tutorial is intended to help you better understand how to handle conflicts that occur when two or more clients write to the same database record in a Windows Store app. Two or more clients may write changes to the same item, at the same time, in some scenarios. Without any conflict detection, the last write would overwrite any previous updates even if this was not the desired result. Azure Mobile Services provides support for detecting and resolving these conflicts. This topic walks you through the steps that allow you to handle database write conflicts on both the server and in your application. In this tutorial you will add functionality to the quickstart app to handle contentions that can occur when updating the TodoItem database. ## Prerequisites This tutorial requires the following + Microsoft Visual Studio 2013 or later. + This tutorial is based on the Mobile Services quickstart. Before you start this tutorial, you must first complete [Get started with Mobile Services]. + [Azure Account] + Azure Mobile Services NuGet Package 1.1.0 or later. To get the latest version, follow these steps below: 1. In Visual Studio, open the project and right-click the project in Solution Explorer then click **Manage Nuget Packages**. ![][19] 2. Expand **Online** and click **Microsoft and .NET**. In the search text box enter **Azure Mobile Services**. Click **Install** on the **Azure Mobile Services** NuGet Package. ![][20] ## Update the application to allow updates In this section you will update the TodoList user interface to allow updating the text of each item in a ListBox control. The ListBox will contain a CheckBox and TextBox control for each item in the database table. You will be able to update the text field of the TodoItem. The application will handle the `LostFocus` event from that TextBox to update the item in the database. 1. In Visual Studio, open the TodoList project you downloaded in the [Get started with Mobile Services] tutorial. 2. In the Visual Studio Solution Explorer, open MainPage.xaml and replace the `ListView` definition with the `ListView` shown below and save the change. 4. In the Visual Studio Solution Explorer, open MainPage.cs in the shared project. Add the event handler to the MainPage for the TextBox `LostFocus` event as shown below. private async void ToDoText_LostFocus(object sender, RoutedEventArgs e) { TextBox tb = (TextBox)sender; TodoItem item = tb.DataContext as TodoItem; //let's see if the text changed if (item.Text != tb.Text) { item.Text = tb.Text; await UpdateToDoItem(item); } } 4. In MainPage.cs for the shared project, add the definition for the MainPage `UpdateToDoItem()` method referenced in the event handler as shown below. private async Task UpdateToDoItem(TodoItem item) { Exception exception = null; try { //update at the remote table await todoTable.UpdateAsync(item); } catch (Exception ex) { exception = ex; } if (exception != null) { await new MessageDialog(exception.Message, "Update Failed").ShowAsync(); } } The application now writes the text changes to each item back to the database when the TextBox loses focus. ## Enable Conflict Detection in your application Two or more clients may write changes to the same item, at the same time, in some scenarios. Without any conflict detection, the last write would overwrite any previous updates even if this was not the desired result. [Optimistic Concurrency Control] assumes that each transaction can commit and therefore does not use any resource locking. Before committing a transaction, optimistic concurrency control verifies that no other transaction has modified the data. If the data has been modified, the committing transaction is rolled back. Azure Mobile Services supports optimistic concurrency control by tracking changes to each item using the `__version` system property column that is added to each table. In this section, we will enable the application to detect these write conflicts through the `__version` system property. The application will be notified by a `MobileServicePreconditionFailedException` during an update attempt if the record has changed since the last query. It will then be able to make a choice of whether to commit its change to the database or leave the last change to the database intact. For more information on the System Properties for Mobile Services, see [System Properties]. 1. Open TodoItem.cs in the shared project and update the `TodoItem` class definition with the following code to include the `__version` system property enabling support for write conflict detection. public class TodoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } [JsonProperty(PropertyName = "__version")] public string Version { set; get; } } > [AZURE.NOTE] When using untyped tables, enable optimistic concurrency by adding the Version flag to the SystemProperties of the table. > >````` >//Enable optimistic concurrency by retrieving __version >todoTable.SystemProperties |= MobileServiceSystemProperties.Version; >````` 2. By adding the `Version` property to the `TodoItem` class, the application will be notified with a `MobileServicePreconditionFailedException` exception during an update if the record has changed since the last query. This exception includes the latest version of the item from the server. In MainPage.cs for the shared project, add the following code to handle the exception in the `UpdateToDoItem()` method. private async Task UpdateToDoItem(TodoItem item) { Exception exception = null; try { //update at the remote table await todoTable.UpdateAsync(item); } catch (MobileServicePreconditionFailedException writeException) { exception = writeException; } catch (Exception ex) { exception = ex; } if (exception != null) { if (exception is MobileServicePreconditionFailedException) { //Conflict detected, the item has changed since the last query //Resolve the conflict between the local and server item await ResolveConflict(item, ((MobileServicePreconditionFailedException) exception).Item); } else { await new MessageDialog(exception.Message, "Update Failed").ShowAsync(); } } } 3. In MainPage.cs, add the definition for the `ResolveConflict()` method referenced in `UpdateToDoItem()`. Notice that in order to resolve the conflict, you set the local item's version to the updated version from the server before committing the user's decision. Otherwise, you will continually encounter the conflict. private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem) { //Ask user to choose the resolution between versions MessageDialog msgDialog = new MessageDialog(String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n", serverItem.Text, localItem.Text), "CONFLICT DETECTED - Select a resolution:"); UICommand localBtn = new UICommand("Commit Local Text"); UICommand ServerBtn = new UICommand("Leave Server Text"); msgDialog.Commands.Add(localBtn); msgDialog.Commands.Add(ServerBtn); localBtn.Invoked = async (IUICommand command) => { // To resolve the conflict, update the version of the // item being committed. Otherwise, you will keep // catching a MobileServicePreConditionFailedException. localItem.Version = serverItem.Version; // Updating recursively here just in case another // change happened while the user was making a decision await UpdateToDoItem(localItem); }; ServerBtn.Invoked = async (IUICommand command) => { RefreshTodoItems(); }; await msgDialog.ShowAsync(); } ## Test database write conflicts in the application In this section you will build a Windows Store app package to install the app on a second machine or virtual machine. Then you will run the app on both machines generating a write conflict to test the code. Both instances of the app will attempt to update the same item's `text` property requiring the user to resolve the conflict. 1. Create a Windows Store app package to install on second machine or virtual machine. To do this, click **Project**->**Store**->**Create App Packages** in Visual Studio. ![][0] 2. On the Create Your Packages screen, click **No** as this package will not be uploaded to the Windows Store. Then click **Next**. ![][1] 3. On the Select and Configure Packages screen, accept the defaults and click **Create**. ![][10] 4. On the Package Creation Completed screen, click the **Output location** link to open the package location. ![][11] 5. Copy the package folder, "todolist_1.0.0.0_AnyCPU_Debug_Test", to the second machine. On that machine, open the package folder and right click on the **Add-AppDevPackage.ps1** PowerShell script and click **Run with PowerShell** as shown below. Follow the prompts to install the app. ![][12] 5. Run instance 1 of the app in Visual Studio by clicking **Debug**->**Start Debugging**. On the Start screen of the second machine, click the down arrow to see "Apps by name". Then click the **todolist** app to run instance 2 of the app. App Instance 1 ![][2] App Instance 2 ![][2] 6. In instance 1 of the app, update the text of the last item to **Test Write 1**, then click another text box so that the `LostFocus` event handler updates the database. The screenshot below shows an example. App Instance 1 ![][3] App Instance 2 ![][2] 7. At this point the corresponding item in instance 2 of the app has an old version of the item. In that instance of the app, enter **Test Write 2** for the `text` property. Then click another text box so the `LostFocus` event handler attempts to update the database with the old `_version` property. App Instance 1 ![][4] App Instance 2 ![][5] 8. Since the `__version` value used with the update attempt didn't match the server `__version` value, the Mobile Services SDK throws a `MobileServicePreconditionFailedException` allowing the app to resolve this conflict. To resolve the conflict, you can click **Commit Local Text** to commit the values from instance 2. Alternatively, click **Leave Server Text** to discard the values in instance 2, leaving the values from instance 1 of the app committed. App Instance 1 ![][4] App Instance 2 ![][6] ## Automatically handling conflict resolution in server scripts You can detect and resolve write conflicts in server scripts. This is a good idea when you can use scripted logic instead of user interaction to resolve the conflict. In this section, you will add a server side script to the TodoItem table for the application. The logic this script will use to resolve conflicts is as follows: + If the TodoItem's ` complete` field is set to true, then it is considered completed and `text` can no longer be changed. + If the TodoItem's ` complete` field is still false, then attempts to update `text` will be comitted. The following steps walk you through adding the server update script and testing it. 1. Log into the [Azure classic portal], click **Mobile Services**, and then click your app. ![][7] 2. Click the **Data** tab, then click the **TodoItem** table. ![][8] 3. Click **Script**, then select the **Update** operation. ![][9] 4. Replace the existing script with the following function, and then click **Save**. function update(item, user, request) { request.execute({ conflict: function (serverRecord) { // Only committing changes if the item is not completed. if (serverRecord.complete === false) { //write the updated item to the table request.execute(); } else { request.respond(statusCodes.FORBIDDEN, 'The item is already completed.'); } } }); } 5. Run the **todolist** app on both machines. Change the TodoItem `text` for the last item in instance 2. Then click another text box so the `LostFocus` event handler updates the database. App Instance 1 ![][4] App Instance 2 ![][5] 6. In instance 1 of the app, enter a different value for the last text property. Then click another text box so the `LostFocus` event handler attempts to update the database with an incorrect `__version` property. App Instance 1 ![][13] App Instance 2 ![][14] 7. Notice that no exception was encountered in the app since the server script resolved the conflict allowing the update since the item is not marked complete. To see that the update was truly successful, click **Refresh** in instance 2 to re-query the database. App Instance 1 ![][15] App Instance 2 ![][15] 8. In instance 1, click the check box to complete the last Todo item. App Instance 1 ![][16] App Instance 2 ![][15] 9. In instance 2, try to update the last TodoItem's text and trigger the `LostFocus` event. In response to the conflict, the script resolved it by refusing the update because the item was already completed. App Instance 1 ![][17] App Instance 2 ![][18] ## Next steps This tutorial demonstrated how to enable a Windows Store app to handle write conflicts when working with data in Mobile Services. Next, consider completing one of the following Windows Store tutorials: * [Add authentication to your app]
Learn how to authenticate users of your app. * [Add push notifications to your app]
Learn how to send a very basic push notification to your app with Mobile Services. [0]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-create-app-package1.png [1]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-create-app-package2.png [2]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app1.png [3]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app1-write1.png [4]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app1-write2.png [5]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app2-write2.png [6]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app2-write2-conflict.png [7]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/mobile-services-selection.png [8]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/mobile-portal-data-tables.png [9]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/mobile-insert-script-users.png [10]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-create-app-package3.png [11]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-create-app-package4.png [12]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-install-app-package.png [13]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app1-write3.png [14]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-app2-write3.png [15]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-write3.png [16]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-checkbox.png [17]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-2-items.png [18]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/Mobile-oc-store-already-complete.png [19]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/mobile-manage-nuget-packages-VS.png [20]: ./media/mobile-services-windows-store-dotnet-handle-database-conflicts/mobile-manage-nuget-packages-dialog.png [Optimistic Concurrency Control]: http://go.microsoft.com/fwlink/?LinkId=330935 [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started/#create-new-service [Azure Account]: http://www.windowsazure.com/pricing/free-trial/ [Validate and modify data with scripts]: https://azure.microsoft.com/develop/mobile/tutorials/validate-modify-and-augment-data-dotnet [Refine queries with paging]: https://azure.microsoft.com/develop/mobile/tutorials/add-paging-to-data-dotnet [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started [Get started with data]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-data-dotnet [Add authentication to your app]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-dotnet [Add push notifications to your app]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-dotnet [Azure classic portal]: https://manage.windowsazure.com/ [Windows Phone 8 SDK]: http://go.microsoft.com/fwlink/p/?LinkID=268374 [Mobile Services SDK]: http://go.microsoft.com/fwlink/p/?LinkID=268375 [Developer Code Samples site]: http://go.microsoft.com/fwlink/p/?LinkId=271146 [System Properties]: http://go.microsoft.com/fwlink/?LinkId=331143 ================================================ FILE: docs/mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md ================================================ # Handling conflicts with offline data sync in Mobile Services >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend)] - [(iOS | Any)](mobile-services-ios-handling-conflicts-offline-data.md) - [(Windows Runtime 8.1 universal C# | Any)](mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md) ## Overview This topic shows you how to synchronize data and handle conflicts when using the offline capabilities of Azure Mobile Services. If you prefer to watch a video, the clip below follows the same steps as this tutorial. > [AZURE.VIDEO build-offline-apps-with-mobile-services] In this tutorial, you download a universal Windows C# solution for an app that supports handling offline synchronization conflicts. You will integrate a mobile service with the app, and then run the Windows Store 8.1 and Windows Phone 8.1 clients to generate a sync conflict and resolve it. This tutorial builds on the steps and the sample app from the previous tutorial [Get started with offline data]. Before you begin this tutorial, you should first complete [Get started with offline data]. ## Prerequisites This tutorial requires Visual Studio 2013 running on Windows 8.1. ## Download the sample project ![][0] This tutorial is a walkthrough of how the [Todo Offline Mobile Services sample] handles sync conflicts between the local offline store and the Mobile Service database in Azure. 1. Download the zip file for the [Mobile Services Samples GitHub Repository] and extract it to a working directory. 2. If you haven't already installed SQLite for Windows 8.1 and Windows Phone 8.1 as mentioned in the [Get started with offline data] tutorial, install both runtimes. 3. In Visual Studio 2013, open the *mobile-services-samples\TodoOffline\WindowsUniversal\TodoOffline-Universal.sln* solution file. Press the **F5** key to rebuild and run the project. Verify the NuGet packages are restored and the references are correctly set. >[AZURE.NOTE] You may need to delete any old references to the SQLite runtime and replace them with the updated reference as mentioned in the [Get started with offline data] tutorial. 4. In the app, type some text in **Insert a TodoItem**, then click **Save** to add some todo items to the local store. Then close the app. Note that the app is not yet connected to any mobile service, so the buttons **Push** and **Pull** will throw exceptions. ## Test the app against your mobile service Now it's time to test the app against Mobile Services. 1. In the [Azure classic portal], find your mobile service's application key by clicking **Manage Keys** on the command bar of the **Dashboard** tab. Copy the **Application Key**. 2. In Solution Explorer for Visual Studio, open the App.xaml.cs file in the client sample project. Change the initialization of the **MobileServiceClient** to use your mobile service URL and application key: public static MobileServiceClient MobileService = new MobileServiceClient( "https://your-mobile-service.azure-mobile.net/", "Your AppKey" ); 3. In Visual Studio, press the **F5** key to build and run the app again. ![][0] ## Update the data in the backend to create a conflict In a real world scenario, a sync conflict would occur when one app pushes updates to a record in the database, and then another app tries to push an update to the same record using an outdated version field in that record. If you recall from the [Get started with offline data], the version system property is required to support the offline syncing features. This version information is examined with each database update. If an instance of the app tries to update a record using an outdated version, a conflict will occur and be caught as a `MobileServicePreconditionFailedException` in the app. If the app doesn't catch the `MobileServicePreconditionFailedException` then a `MobileServicePushFailedException` will end up being thrown describing how many sync errors were encountered. >[AZURE.NOTE] To support synchronization of deleted records with offline data sync, you should enable [Soft Delete](mobile-services-using-soft-delete.md). Otherwise, you have to manually remove records in the local store, or call `IMobileServiceSyncTable::PurgeAsync()` to purge the local store. The following steps show the Windows Phone 8.1 and Windows Store 8.1 clients running at the same time to cause and resolve a conflict using the sample. 1. In Visual Studio, right click the Windows Phone 8.1 project and click **Set as Startup Project**. Then press **Ctrl+F5** keys to run the Windows Phone 8.1 client without debugging. Once you have the Windows Phone 8.1 client up and running in the emulator, click the **Pull** button to sync the local store with the current state of the database. ![][3] 2. In Visual Studio, right click the Windows 8.1 runtime project and click **Set as Startup Project** to set it back to the start up project. Then press **F5** to run it. Once you have the Windows Store 8.1 client up and running, click the **Pull** button to sync the local store with the current state of the database. ![][4] 3. At this point point both clients are synchronized with the database. The code for both clients are also using incremental sync, so that they will only sync incomplete todo items. Completed todo items will be ignored. Choose one of the items and edit the text of the same item in both clients to a different value. Click the **Push** button to sync both changes with the database on the server. ![][5] ![][6] 4. The client whose push was executing last encounters the conflict and allows the user to decide which value to commit to the database. The exception provides the correct version value which is used for resolving the conflict. ![][7] ## Review of the code for handling sync conflicts In order to use the offline features for Mobile Services, you must include the version column in both your local database and your data transfer object. This is accomplished by updating the `TodoItem` class the following member: [Version] public string Version { get; set; } The `__version` column is included in the local database in the `OnNavigatedTo()` method when the `TodoItem` class is used to define the local store. To handle offline sync conflicts in your code, you create a class that implements `IMobileServiceSyncHandler`. Pass an object of this type in the call to `MobileServiceClient.SyncContext.InitializeAsync()`. This also occurs in the `OnNavigatedTo()` method of the sample. await App.MobileService.SyncContext.InitializeAsync(store, new SyncHandler(App.MobileService)); The class `SyncHandler` in **SyncHandler.cs** implements `IMobileServiceSyncHandler`. The method `ExecuteTableOperationAsync` is called when each push operation is sent to the server. If an exception of type `MobileServicePreconditionFailedException` is thrown, this means that there is a conflict between the local and remote versions of an item. To resolve conflicts in favor of the local item, you should simply retry the operation. Once a conflict has occurred, the local item version will be updated to match the server version, so executing the operation again will overwrite the server changes with the local changes: await operation.ExecuteAsync(); To resolve conflicts in favor of the server item, simply return from the `ExecuteTableOperationAsync`. The local version of the object will be discarded and replaced with the value from the server. To stop the push operation (but retain the queued changes), use the method `AbortPush()`: operation.AbortPush(); This will stop the current push operation but will keep all pending changes, including the current operation if `AbortPush` is called from `ExecuteTableOperationAsync`. The next time that `PushAsync()` is called, these changes will be sent to the server. When a push is canceled, `PushAsync` will throw a `MobileServicePushFailedException`, and the exception property `PushResult.Status` will have the value `MobileServicePushStatus.CancelledByOperation`. [0]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/mobile-services-handling-conflicts-app-run1.png [1]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/javascript-backend-database.png [2]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/dotnet-backend-database.png [3]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/wp81-view.png [4]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/win81-view.png [5]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/wp81-edit-text.png [6]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/win81-edit-text.png [7]: ./media/mobile-services-windows-store-dotnet-handling-conflicts-offline-data/conflict.png [Handling conflicts code sample]: http://go.microsoft.com/fwlink/?LinkId=394787 [Get started with Mobile Services]: mobile-services-windows-store-get-started.md [Get started with offline data]: mobile-services-windows-store-dotnet-get-started-offline-data.md [SQLite for Windows 8.1]: http://go.microsoft.com/fwlink/?LinkId=394776 [Azure classic portal]: https://manage.windowsazure.com/ [Handling Database Conflicts]: mobile-services-windows-store-dotnet-handle-database-conflicts.md#test-app [Mobile Services Samples GitHub Repository]: http://go.microsoft.com/fwlink/?LinkId=512865 [Todo Offline Mobile Services sample]: http://go.microsoft.com/fwlink/?LinkId=512866 ================================================ FILE: docs/mobile-services-xamarin-android-get-started-offline-data.md ================================================ # Using offline data sync in Mobile Services > [AZURE.SELECTOR] - [Android)](mobile-services-android-get-started-offline-data.md) - [iOS](mobile-services-ios-get-started-offline-data.md) - [Windows](mobile-services-windows-store-dotnet-get-started-offline-data.md) - [Xamarin.Android](mobile-services-xamarin-android-get-started-offline-data.md) - [Xamarin.iOS](mobile-services-xamarin-ios-get-started-offline-data.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic walks through the offline sync capabilities of Azure Mobile Services in the todo list quickstart app. Offline sync allows you to easily create apps that are usable even when the end user has no network access. Offline sync has several potential uses: * Improve app responsiveness by caching server data locally on the device * Make apps resilient against intermittent network connectivity * Allow end-users to create and modify data even when there is no network access, supporting scenarios with little or no connectivity * Sync data across multiple devices and detect conflicts when the same record is modified by two devices >[AZURE.NOTE] To complete this tutorial, you need a Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see Azure Free Trial. > > If this is your first experience with Mobile Services, you should first complete [Get started with Mobile Services]. This tutorial walks you through these basic steps: 1. [Review the Mobile Services sync code] 2. [Update the sync behavior of the app] 3. [Update the app to reconnect your mobile service] This tutorial requires the following: * Visual Studio with Xamarin on Windows or Xamarin Studio on Mac OS X. Complete installation instructions are on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). * Completion of the [Get started with Mobile Services] tutorial. ## Review the Mobile Services sync code Azure Mobile Services offline sync allows end users to interact with a local database when the network is not accessible. To use these features in your app, you initialize `MobileServiceClient.SyncContext` to a local store. Then reference your table through the `IMobileServiceSyncTable` interface. This section walks through the offline sync related code in `ToDoActivity.cs`. 1. In Visual Studio or Xamarin Studio, open the project that you completed in the [Get started with Mobile Services] tutorial. Open the file `ToDoActivity.cs`. 2. Notice the type of the member `toDoTable` is `IMobileServiceSyncTable`. Offline sync uses this sync table interface instead of `IMobileServiceTable`. When a sync table is used, all operations go to the local store and are only synchronized with the remote service with explicit push and pull operations. To get a reference to a sync table, the method `GetSyncTable()` is used. To remove the offline sync functionality, you would instead use `GetTable()`. 3. Before any table operations can be performed, the local store must be initialized. This is done in the `InitLocalStoreAsync` method: private async Task InitLocalStoreAsync() { // new code to initialize the SQLite store string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), localDbFilename); if (!File.Exists(path)) { File.Create(path).Dispose(); } var store = new MobileServiceSQLiteStore(path); store.DefineTable(); // Uses the default conflict handler, which fails on conflict await client.SyncContext.InitializeAsync(store); } This creates a local store using the class `MobileServiceSQLiteStore`, which is provided in the Mobile Services SDK. You can also a provide a different local store implementation by implementing `IMobileServiceLocalStore`. The `DefineTable` method creates a table in the local store that matches the fields in the provided type, `ToDoItem` in this case. The type doesn't have to include all of the columns that are in the remote database--it is possible to store just a subset of columns. This overload of `InitializeAsync` uses the default conflict handler, which fails whenever there is a conflict. To provide a custom conflict handler, see the tutorial [Handling conflicts with offline support for Mobile Services]. 4. The method `SyncAsync` triggers the actual sync operation: private async Task SyncAsync() { await client.SyncContext.PushAsync(); await toDoTable.PullAsync("allTodoItems", toDoTable.CreateQuery()); // query ID is used for incremental sync } First, there is a call to `IMobileServiceSyncContext.PushAsync()`. This method is a member of `IMobileServicesSyncContext` instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server. Next, the method calls `IMobileServiceSyncTable.PullAsync()` to pull data from a table on the server to the app. Note that if there are any changes pending in the sync context, a pull always issues a push first. This is to ensure all tables in the local store along with relationships are consistent. In this case, we have called push explicitly. In this example, we retrieve all records in the remote `TodoItem` table, but it is also possible to filter records by passing a query. The first parameter to `PullAsync()` is a query ID that is used for incremental sync, which uses the `UpdatedAt` timestamp to get only those records modified since the last sync. The query ID should be a descriptive string that is unique for each logical query in your app. To opt-out of incremental sync, pass `null` as the query ID. This will retrieve all records on each pull operation, which is potentially inefficient. >[AZURE.NOTE] To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. Note that the `MobileServicePushFailedException` can occur for both a push and a pull operation. The next tutorial, [Handling conflicts with offline support for Mobile Services], shows how to handle these sync related exceptions. 5. In the class `ToDoActivity`, the method `SyncAsync()` is called after the operations that modify data, `AddItem()` and `CheckItem()`. It is also called from `OnRefreshItemsSelected()`, so that users get the latest data whenever they push the **Refresh** button. The app also performs a sync on launch, since `ToDoActivity.OnCreate()` calls `OnRefreshItemsSelected()`. Because `SyncAsync()` is called whenever data is modified, this app assumes that the user is online whenever they are editing data. In the next section, we will update the app so that users can edit even when they are offline. ## Update the sync behavior of the app In this section, you will modify the app so that it does not sync on app launch or on the insert and update operations, but only when the refresh button is pushed. Then, you will break the app connection with the mobile service to simulate an offline scenario. When you add data items, they will be held in the local store, but not immediately synced to the mobile service. 1. In the class `ToDoActivity`, edit the methods `AddItem()` and `CheckItem()` to comment out the calls to `SyncAsync()`. 2. In `ToDoActivity`, comment out the definitions of the members `applicationURL` and `applicationKey`. Add the following lines, which reference an invalid mobile service URL: const string applicationURL = @"https://your-mobile-service.azure-mobile.xxx/"; const string applicationKey = @"AppKey"; 3. In `ToDoActivity.OnCreate()`, remove the call to `OnRefreshItemsSelected()` and replace with: // Load the items from the Mobile Service // OnRefreshItemsSelected (); // don't sync on app launch await RefreshItemsFromTableAsync(); // load UI only 4. Build and run the app. Add some new todo items. These new items exist only in the local store until they can be pushed to the mobile service. The client app behaves as if is connected to the mobile service supporting all create, read, update, delete (CRUD) operations. 5. Close the app and restart it to verify that the new items you created are persisted to the local store. ## Update the app to reconnect your mobile service In this section you will reconnect the app to the mobile service. This simulates the app moving from an offline state to an online state with the mobile service. When you push the **Refresh** button, data will be synced to your mobile service. 1. Open `ToDoActivity.cs`. Remove the invalid mobile service URL and add back the correct URL and app key. 2. Rebuild and run the app. Notice that the data looks the same as the offline scenario even though the app is now connected to the mobile service. This is because this app always uses the `IMobileServiceSyncTable` that is pointed to the local store. 3. Log into the [Azure classic portal] and look at the database for your mobile service. If your service uses the JavaScript backend, you can browse the data from the **Data** tab of the mobile service. If you are using the .NET backend for your mobile service, in Visual Studio go to **Server Explorer** > **Azure** > **SQL Databases**. Right click your database and select **Open in SQL Server Object Explorer**. Notice the data has *not* been synchronized between the database and the local store. 4. In the app, push the refresh button. This calls `OnRefreshItemsSelected()`, which in turn calls `SyncAsync()`. This will perform the push and pull operations, first sending the local store items to the mobile service, then retrieving new data from the service. 5. Check the database for your mobile service to confirm that changes have been synchronized. ## Summary In order to support the offline features of mobile services, we used the `IMobileServiceSyncTable` interface and initialized `MobileServiceClient.SyncContext` with a local store. In this case the local store was a SQLite database. The normal CRUD operations for mobile services work as if the app is still connected but, all the operations occur against the local store. When we wanted to synchronize the local store with the server, we used the `IMobileServiceSyncTable.PullAsync` and `MobileServiceClient.SyncContext.PushAsync` methods. * To push changes to the server, we called `IMobileServiceSyncContext.PushAsync()`. This method is a member of `IMobileServicesSyncContext` instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server. * To pull data from a table on the server to the app, we called `IMobileServiceSyncTable.PullAsync`. A pull always issues a push first. This is to ensure all tables in the local store along with relationships remain consistent. There are also overloads of `PullAsync()` that allow a query to be specified in order to filter the data that is stored on the client. If a query is not passed, `PullAsync()` will pull all rows in the corresponding table (or query). You can pass the query to filter only the changes your app needs to sync with. * To enable incremental sync, pass a query ID to `PullAsync()`. The query ID is used to store the last updated timestamp from the results of the last pull operation. The query ID should be a descriptive string that is unique for each logical query in your app. If the query has a parameter, then the same parameter value has to be part of the query ID. For instance, if you are filtering on userid, it needs to be part of the query ID: await PullAsync("todoItems" + userid, syncTable.Where(u => u.UserId = userid)); If you want to opt out of incremental sync, pass `null` as the query ID. In this case, all records will be retrieved on every call to `PullAsync`, which is potentially inefficient. * To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. ## Next steps * [Handling conflicts with offline support for Mobile Services] * [How to use the Xamarin Component client for Azure Mobile Services] [Review the Mobile Services sync code]: #review-offline [Update the sync behavior of the app]: #update-sync [Update the app to reconnect your mobile service]: #update-online-app [Handling conflicts with offline support for Mobile Services]: mobile-services-windows-store-dotnet-handling-conflicts-offline-data.md [Get started with Mobile Services]: mobile-services-android-get-started.md [How to use the Xamarin Component client for Azure Mobile Services]: partner-xamarin-mobile-services-how-to-use-client-library.md [Soft Delete]: mobile-services-using-soft-delete.md [Mobile Services SDK Nuget]: http://www.nuget.org/packages/WindowsAzure.MobileServices/1.3.0 [SQLite store nuget]: http://www.nuget.org/packages/WindowsAzure.MobileServices.SQLiteStore/1.0.0 [Azure classic portal]: https://manage.windowsazure.com ================================================ FILE: docs/mobile-services-xamarin-ios-get-started-offline-data.md ================================================ # Using offline data sync in Mobile Services > [AZURE.SELECTOR] - [Android)](mobile-services-android-get-started-offline-data.md) - [iOS](mobile-services-ios-get-started-offline-data.md) - [Windows](mobile-services-windows-store-dotnet-get-started-offline-data.md) - [Xamarin.Android](mobile-services-xamarin-android-get-started-offline-data.md) - [Xamarin.iOS](mobile-services-xamarin-ios-get-started-offline-data.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic walks through the offline sync capabilities of Azure Mobile Services in the todo list quickstart app. Offline sync allows you to easily create apps that are usable even when the end user has no network access. Offline sync has several potential uses: * Improve app responsiveness by caching server data locally on the device * Make apps resilient against intermittent network connectivity * Allow end-users to create and modify data even when there is no network access, supporting scenarios with little or no connectivity * Sync data across multiple devices and detect conflicts when the same record is modified by two devices >[AZURE.NOTE] To complete this tutorial, you need a Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see Azure Free Trial. > > If this is your first experience with Mobile Services, you should first complete [Get started with Mobile Services]. This tutorial walks you through these basic steps: 1. [Review the Mobile Services sync code] 2. [Update the sync behavior of the app] 3. [Update the app to reconnect your mobile service] This tutorial requires the following: * Visual Studio with Xamarin. See [Setup and install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx) for instructions. * A Mac with Xcode v7.0 or later and Xamarin Studio Community installed. See [Setup and install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx) and [Setup, install, and verifications for Mac users](https://msdn.microsoft.com/library/mt488770.aspx) (MSDN). * Completion of the [Get started with Mobile Services] tutorial. ## Review the Mobile Services sync code Azure Mobile Services offline sync allows end users to interact with a local database when the network is not accessible. To use these features in your app, you initialize `MobileServiceClient.SyncContext` to a local store. Then reference your table through the `IMobileServiceSyncTable` interface. This section walks through the offline sync related code in `QSTodoService.cs`. 1. In Visual Studio, open the project that you completed in the [Get started with Mobile Services] tutorial. Open the file `QSTodoService.cs`. 2. Notice the type of the member `todoTable` is `IMobileServiceSyncTable`. Offline sync uses this sync table interface instead of `IMobileServiceTable`. When a sync table is used, all operations go to the local store and are only synchronized with the remote service with explicit push and pull operations. To get a reference to a sync table, the method `GetSyncTable()` is used. To remove the offline sync functionality, you would instead use `GetTable()`. 3. Before any table operations can be performed, the local store must be initialized. This is done in the `InitializeStoreAsync` method: public async Task InitializeStoreAsync() { var store = new MobileServiceSQLiteStore(localDbPath); store.DefineTable(); // Uses the default conflict handler, which fails on conflict await client.SyncContext.InitializeAsync(store); } This creates a local store using the class `MobileServiceSQLiteStore`, which is provided in the Mobile Services SDK. You can also provide a different local store implementation by implementing `IMobileServiceLocalStore`. The `DefineTable` method creates a table in the local store that matches the fields in the provided type, `ToDoItem` in this case. The type doesn't have to include all of the columns that are in the remote database--it is possible to store just a subset of columns. This overload of `InitializeAsync` uses the default conflict handler, which fails whenever there is a conflict. To provide a custom conflict handler, see the tutorial [Handling conflicts with offline support for Mobile Services]. 4. The method `SyncAsync` triggers the actual sync operation: public async Task SyncAsync() { try { await client.SyncContext.PushAsync(); await todoTable.PullAsync("allTodoItems", todoTable.CreateQuery()); // query ID is used for incremental sync } catch (MobileServiceInvalidOperationException e) { Console.Error.WriteLine(@"Sync Failed: {0}", e.Message); } } First, there is a call to `IMobileServiceSyncContext.PushAsync()`. This method is a member of `IMobileServicesSyncContext` instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server. Next, the method calls `IMobileServiceSyncTable.PullAsync()` to pull data from a table on the server to the app. Note that if there are any changes pending in the sync context, a pull always issues a push first. This is to ensure all tables in the local store along with relationships are consistent. In this case, we have called push explicitly. In this example, we retrieve all records in the remote `TodoItem` table, but it is also possible to filter records by passing a query. The first parameter to `PullAsync()` is a query ID that is used for incremental sync, which uses the `UpdatedAt` timestamp to get only those records modified since the last sync. The query ID should be a descriptive string that is unique for each logical query in your app. To opt-out of incremental sync, pass `null` as the query ID. This will retrieve all records on each pull operation, which is potentially inefficient. >[AZURE.NOTE] To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. Note that the `MobileServicePushFailedException` can occur for both a push and a pull operation. The next tutorial, [Handling conflicts with offline support for Mobile Services], shows how to handle these sync related exceptions. 5. In the class `QSTodoService`, the method `SyncAsync()` is called after the operations that modify data, `InsertTodoItemAsync()` and `CompleteItemAsync`. It is also called from `RefreshDataAsync()`, so that the user gets the latest data whenever they perform the refresh gesture. The app also performs a sync on launch, since `QSTodoListViewController.ViewDidLoad()` calls `RefreshDataAsync()`. Because `SyncAsync()` is called whenever data is modified, this app assumes that the user is online whenever they are editing data. In the next section, we will update the app so that users can edit even when they are offline. ## Update the sync behavior of the app In this section, you will modify the app so that it does not sync on app launch or on the insert and update operations, but only when the refresh gesture is performed. Then, you will break the app connection with the mobile service to simulate an offline scenario. When you add data items, they will be held in the local store, but not immediately synced to the mobile service. 1. Open `QSTodoService.cs`. Comment out the calls to `SyncAsync()` in the following methods: - `InsertTodoItemAsync` - `CompleteItemAsync` - `RefreshDataAsync` Now, `RefreshAsync()` will only load data from the local store, but will not connect to the app backend. 2. In `QSTodoService.cs`, comment out the definitions of the members `applicationURL` and `applicationKey`. Add the following lines, which reference an invalid mobile service URL: const string applicationURL = @"https://your-mobile-service.azure-mobile.xxx/"; const string applicationKey = @"AppKey"; 3. To ensure that data is synchronized when the refresh gesture is performed, edit the method `QSTodoListViewController.RefreshAsync()`. Add a call to `SyncAsync()` before the call to `RefreshDataAsync()`: private async Task RefreshAsync () { RefreshControl.BeginRefreshing (); await todoService.SyncAsync(); await todoService.RefreshDataAsync (); // add this line RefreshControl.EndRefreshing (); TableView.ReloadData (); } 4. Build and run the app. Add some new todo items. These new items exist only in the local store until they can be pushed to the mobile service. The client app behaves as if is connected to the mobile service supporting all create, read, update, delete (CRUD) operations. 5. Close the app and restart it to verify that the new items you created are persisted to the local store. ## Update the app to reconnect your mobile service In this section you will reconnect the app to the mobile service. This simulates the app moving from an offline state to an online state with the mobile service. When you perform the refresh gesture, data will be synced to your mobile service. 1. Open `QSTodoService.cs`. Remove the invalid mobile service URL and add back the correct URL and app key. 2. Rebuild and run the app. Notice that the data looks the same as the offline scenario even though the app is now connected to the mobile service. This is because this app always uses the `IMobileServiceSyncTable` that is pointed to the local store. 3. Log into the [Azure classic portal] and look at the database for your mobile service. If your service uses the JavaScript backend, you can browse the data from the **Data** tab of the mobile service. If you are using the .NET backend for your mobile service, in Visual Studio go to **Server Explorer** > **Azure** > **SQL Databases**. Right click your database and select **Open in SQL Server Object Explorer**. Notice the data has *not* been synchronized between the database and the local store. 4. In the app, perform the refresh gesture by pulling down the list of items. This causes the app to call `RefreshDataAsync()`, which in turn calls `SyncAsync()`. This will perform the push and pull operations, first sending the local store items to the mobile service, then retrieving new data from the service. 5. Check the database for your mobile service to confirm that changes have been synchronized. ## Summary In order to support the offline features of mobile services, we used the `IMobileServiceSyncTable` interface and initialized `MobileServiceClient.SyncContext` with a local store. In this case the local store was a SQLite database. The normal CRUD operations for mobile services work as if the app is still connected but, all the operations occur against the local store. When we wanted to synchronize the local store with the server, we used the `IMobileServiceSyncTable.PullAsync` and `MobileServiceClient.SyncContext.PushAsync` methods. * To push changes to the server, we called `IMobileServiceSyncContext.PushAsync()`. This method is a member of `IMobileServicesSyncContext` instead of the sync table because it will push changes across all tables. Only records that have been modified in some way locally (through CUD operations) will be sent to the server. * To pull data from a table on the server to the app, we called `IMobileServiceSyncTable.PullAsync`. A pull always issues a push first. This is to ensure all tables in the local store along with relationships remain consistent. There are also overloads of `PullAsync()` that allow a query to be specified in order to filter the data that is stored on the client. If a query is not passed, `PullAsync()` will pull all rows in the corresponding table (or query). You can pass the query to filter only the changes your app needs to sync with. * To enable incremental sync, pass a query ID to `PullAsync()`. The query ID is used to store the last updated timestamp from the results of the last pull operation. The query ID should be a descriptive string that is unique for each logical query in your app. If the query has a parameter, then the same parameter value has to be part of the query ID. For instance, if you are filtering on userid, it needs to be part of the query ID: await PullAsync("todoItems" + userid, syncTable.Where(u => u.UserId = userid)); If you want to opt out of incremental sync, pass `null` as the query ID. In this case, all records will be retrieved on every call to `PullAsync`, which is potentially inefficient. * To remove records from the device local store when they have been deleted in your mobile service database, you should enable [Soft Delete]. Otherwise, your app should periodically call `IMobileServiceSyncTable.PurgeAsync()` to purge the local store. ## Next steps * [How to use the Xamarin Component client for Azure Mobile Services] [Review the Mobile Services sync code]: #review-offline [Update the sync behavior of the app]: #update-sync [Update the app to reconnect your mobile service]: #update-online-app [Handling conflicts with offline support for Mobile Services]: mobile-services-xamarin-ios-handling-conflicts-offline-data.md [Get started with Mobile Services]: mobile-services-ios-get-started.md [How to use the Xamarin Component client for Azure Mobile Services]: partner-xamarin-mobile-services-how-to-use-client-library.md [Soft Delete]: mobile-services-using-soft-delete.md [Azure classic portal]: https://manage.windowsazure.com ================================================ FILE: docs/partner-sencha-mobile-services-get-started.md ================================================ # Get started with Mobile Services and Sencha Touch >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.TIP] This topic shows you how to get started with Mobile Services as quickly as possible. It is designed for customers new to this Azure feature. If you are already familiar with Mobile Services or are looking for more in-depth information, please select a topic from the left-navigation or see the relevant links in [Next steps](#next-steps). ## Overview This tutorial shows you how to leverage Azure Mobile Services in your Sencha Touch application. You will create a simple *To Do List* app using Sencha Touch that utilizes a mobile service you define through the Azure classic portal. This tutorial is intended for intermediate to advanced web application developers who have a good understanding of JavaScript and who are familiar with the Sencha Touch framework. If you prefer to watch a video, this clip follows the same steps as this tutorial. In the video, Arthur Kay explains how to build a Sencha Touch application using an Azure Mobile Services backend. > [AZURE.VIDEO getting-started-with-sencha-touch] A screenshot from the completed app is shown below: ![][0] ## Requirements - Download and install [Sencha Touch](http://wwww.sencha.com/products/touch/download" target="_blank"). - Download and install [Sencha Cmd Tool](http://www.sencha.com/products/sencha-cmd/download" target="_blank"). - Java Runtime Environment (JRE), or Java Development Kit (if you are creating Android apps) - Ruby and SASS gem. ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a TodoItems Table Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to create a new database table for use in your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **HTML** under **Choose platform** and expand **Create a new HTML app**. ![Mobile quickstart html](./media/partner-sencha-mobile-services-get-started/mobile-portal-quickstart-html.png) This displays the three easy steps to create and host an HTML app connected to your mobile service. ![Mobile quickstart html](./media/partner-sencha-mobile-services-get-started/mobile-quickstart-steps-html.png) 3. Click **Create TodoItems table** to create a table to store app data. > [AZURE.NOTE] Do NOT download the HTML app from the Azure classic portal. Instead, we will manually create a Sencha Touch application in the section below. 1. Take note of the **appKey** and **appUrl** in the Azure classic portal. You will use them in other sections of this tutorial. ![app key](./media/partner-sencha-mobile-services-get-started/mobile-app-key-portal.png) 1. In the **Configure** tab, verify that `localhost` is already listed in the **Allow requests from host names** list under **Cross-Origin Resource Sharing (CORS)**. If it's not, type `localhost` in the **Host name** field and then click **Save**. ![Setup CORS for localhost](./media/partner-sencha-mobile-services-get-started/mobile-services-set-cors-localhost.png) ## Generate your Touch application Generating a Sencha Touch template application is a simple task using Sencha Cmd and is a great way to get an application up and running very quickly. From the directory where you installed the Touch framework, issue the following command: $ sencha generate app Basic /path/to/application This generates a template Touch application with an application name of 'Basic'. To launch your application, simply point your browser to the directory /path/to/application and you should be presented with the standard Touch sample application. ## Installing the Sencha Touch Extensions for Azure The extension for Azure is installed either manually or as a Sencha Package. The method you use is totally up to you. ### Manual installation In most Touch applications, if you wish to add an external library of classes, you simply download the package, unpack it in your application directory and configure the Touch loader with the location of the library. You can manually add the Azure extensions to your application using the following steps: 1. Download the Azure extensions package from [here](https://market.sencha.com/extensions/sencha-extensions-for-microsoft-azure). (You may use your Sencha Forums ID to access this area.) 2. Copy the Azure extensions package from the download directory to where you would ultimately want it to reside and unpack it : $ cd /path/to/application $ mv /download-location/azure.zip . $ unzip azure.zip This creates an **azure** directory containing the entire package source, examples and documentation. The source will reside in the **azure/src** directory. ### Installation as a Sencha package > [AZURE.NOTE] You can only use this method when you have generated your application using the sencha generate app command. All applications generated by Sencha Cmd have a "packages" folder at the root. The location of this folder can be configured, but regardless of its location, the role of the "packages" folder is to serve as the storage of all packages used by your application (or applications if you have created a Sencha Workspace). As Ext.Azure is a Sencha Cmd "package", the source code can be easily installed and included in your application using Sencha Cmd. (See [Sencha Cmd Packages](http://docs.sencha.com/cmd/6.x/cmd_packages/cmd_packages.html) for more information). To download and install the Azure extensions package from the Sencha Packages repository, you will need to add the package name to your **app.json** file and build your application: 1. Add the Azure package to the requires section of your app.json file: { "name": "Basic", "requires": [ "touch-azure" ] } 2. Rebuild your application using **sencha cmd** to fetch and install the package: $ sencha app build Both **sencha app build** and **sencha app refresh** will both now perform the steps needed to integrate the package in to your application. Typically, after changing package requirements, you will need to run **sencha app refresh** so that the metadata required to support "dev mode" is up to date. Whichever command you run, Sencha Cmd will download and expand the package to your "packages" folder. After this you will find a "packages/touch-azure" folder in your workspace. ## Include and configure Azure **Filename**: app.js Now that the Azure extension has been downloaded and installed in your application directory, the next step is to tell your application where to find the source files, and to require those files: 1. Configure the Sencha Loader with the location of the source code: Ext.Loader.setConfig({ enabled : true, paths : { 'Ext' : 'touch/src', 'Ext.azure' : '/path-to/azure-for-touch/azure/src' } }); 2. Require the Azure class files: Ext.application({ requires: [ 'Ext.azure.Azure' ], // ... }); 3. Configuring Azure The Azure package is initialized by calling the **Ext.Azure.init** method in the launch section of your application. This method is passed a configuration object containing mobile service credentials as well as other credentials and features you wish to utilize. While you can pass the configuration object directly to the init method, we suggest creating a Sencha application configuration property called **azure** and placing all the appropriate information there. You can then pass this property value to the Ext.Azure.init method. When you create a mobile service in Azure (see [Getting Started with Azure](http://senchaazuredocs.azurewebsites.net/#!/guide/getting_started)), an application key and URL are assigned to that service. This information must be provided to your Azure package so it can connect to your service. This example shows a very simple Azure configuration and initialization supplying only the application key and URL: Ext.application({ name: 'Basic', requires: [ 'Ext.azure.Azure' ], azure: { appKey: 'myazureservice-access-key', appUrl: 'myazure-service.azure-mobile.net' }, launch: function() { // Call Azure initialization Ext.Azure.init(this.config.azure); } }); For more information on the Azure configuration options, please consult the Ext.Azure API documentation. Congratulations! Your application should now have access to your mobile service. ## Build the ToDo app Now that we have configured your application to include the Azure extension, and provided it with your mobile service credentials, we can move on to creating a Touch application which utilizes your mobile service for viewing and editing your ToDo list data stored in the service. ### Configure the Azure data proxy **Filename:** app/model/TodoItem.js Your Touch application will be communicating with your mobile service via a data proxy. The proxy does all the work of both sending requests to, and receiving data from, the mobile service. Used in combination with a Touch data model and store, all the hard work of processing remote data and getting it into your application is removed and handled by Touch itself. Sencha Touch models provide the definition of the data records you will be using in your application. They allow you to not only define the data fields but also provide configuration about the proxy that will be handling the communication between the application and the Azure mobile service. In the code below you can see that we define the fields (and their types) for the model, and also provide a proxy configuration. When configuring your proxy, you need to give it a type (in this case 'azure'), the mobile service tablename (ToDoItem) and other optional parameters. In this example, we will be turning on proxy paging so that we can seamlessly page forward and backward through list items. The Azure proxy will automatically set all HTTP headers with the appropriate CRUD operations expected by the Azure API (including authentication credentials, if they exist). Ext.define('Basic.model.TodoItem', { extend : 'Ext.data.Model', requires : [ 'Ext.azure.Proxy' ], config : { idProperty : 'id', useCache : false, fields : [ { name : 'id', type : 'int' }, { name : 'text', type : 'string' }, { name : 'complete', type : 'boolean' } ], proxy : { type : 'azure', tableName : 'TodoItem', enablePagingParams : true } } }); ### Store your ToDo items **Filename**: app/store/TodoItems.js Sencha Touch stores are used to store collections of data records (models) which can be used as sources for Touch components for displaying the records in a variety of different ways. This can include Grids, Charts, Lists and more. Here we define a store which will be used to hold all your store ToDo list items which are retrieved from your Azure mobile service. Notice that the store configuration contains the name of the model type (Basic.model.TodoItem - defined above). This defines the structure of the records which will be contained in the store. We also have some additional configuration options for the store such as specifying the page size (8 records), and that the sorting of records for this store is done remotely by the Azure mobile service (no sorting is done locally within the store itself). Ext.define('Basic.store.TodoItems', { extend : 'Ext.data.Store', requires : [ 'Basic.model.TodoItem' ], config : { model : 'Basic.model.TodoItem', pageSize : 8, remoteSort : true, remoteFilter : true } }); ### View and edit your ToDo items **Filename**: app/view/DataItem.js Now that we have defined the structure of each ToDo item, and created a store to place all the records in, we should think about how we wish to display this information to the user of the app. We normally display information to the user through the use of **Views**. A view can be one of any number of Touch components, individually or combined with others. The view below is comprised of a ListItem which defines how each record will be displayed along with some buttons which will accommodate actions to delete each item. Ext.define('Basic.view.DataItem', { extend : 'Ext.dataview.component.ListItem', xtype : 'basic-dataitem', requires : [ 'Ext.Button', 'Ext.layout.HBox', 'Ext.field.Checkbox' ], config : { checkbox : { docked : 'left', xtype : 'checkboxfield', width : 50, labelWidth : 0 }, text : { flex : 1 }, button : { docked : 'right', xtype : 'button', ui : 'plain', iconMask : true, iconCls : 'delete', style : 'color: red;' }, dataMap : { getText : { setHtml : 'text' }, getCheckbox : { setChecked : 'complete' } }, layout : { type : 'hbox', align: 'stretch' } }, applyCheckbox : function(config) { return Ext.factory(config, Ext.field.Checkbox, this.getCheckbox()); }, updateCheckbox : function (cmp) { if (cmp) { this.add(cmp); } }, applyButton : function(config) { return Ext.factory(config, Ext.Button, this.getButton()); }, updateButton : function (cmp) { if (cmp) { this.add(cmp); } } }); ### Create your primary view **Filename**: app/view/Main.js Now that we have defined the layout of an individual ToDo list item (above) we want to wrap a full user interface around that list which defines the actual list of items, an application title, and a button to add a new task. Ext.define('Basic.view.Main', { extend : 'Ext.dataview.List', xtype : 'main', requires : [ 'Ext.TitleBar', 'Ext.dataview.List', 'Ext.data.Store', 'Ext.plugin.PullRefresh', 'Ext.plugin.ListPaging', 'Basic.view.DataItem' ], config : { store : 'TodoItems', useSimpleItems : false, defaultType : 'basic-dataitem', plugins : [ { xclass : 'Ext.plugin.PullRefresh', pullRefreshText : 'Pull down to refresh!' }, { xclass : 'Ext.plugin.ListPaging', autoPaging : true } ], scrollable : { direction : 'vertical', directionLock : true }, items : [ { docked : 'top', xtype : 'titlebar', title : 'Azure Mobile - Basic Data Example' }, { xtype : 'toolbar', docked : 'bottom', items : [ { xtype : 'textfield', placeHolder : 'Enter new task', flex : 1 }, { xtype : 'button', action : 'add', text : 'Add' } ] } ] } }); ### Make everything work together **Filename**: app/controller/Main.js The final step in our application is to respond to button presses (delete, save, etc) and provide the logic behind all these requests. Sencha Touch utilizes controllers which listen for these events and responds accordingly. Ext.define('Basic.controller.Main', { extend : 'Ext.app.Controller', config : { refs : { todoField : 'main toolbar textfield', main : 'main' }, control : { 'button[action=add]' : { tap : 'onAddItem' }, 'button[action=reload]' : { tap : 'onReload' }, main : { activate : 'loadInitialData', itemdoubletap : 'onItemEdit' }, 'basic-dataitem checkboxfield' : { change : 'onItemCompleteTap' }, 'basic-dataitem button' : { tap : 'onItemDeleteTap' } } }, loadInitialData : function () { Ext.getStore('TodoItems').load(); }, onItemDeleteTap : function (button, e, eOpts) { var store = Ext.getStore('TodoItems'), dataItem = button.up('dataitem'), rec = dataItem.getRecord(); rec.erase({ success: function (rec, operation) { store.remove(rec); }, failure: function (rec, operation) { Ext.Msg.alert( 'Error', Ext.util.Format.format('There was an error deleting this task.

Status Code: {0}
Status Text: {1}', operation.error.status, operation.error.statusText) ); } }); }, onItemCompleteTap : function (checkbox, newVal, oldVal, eOpts) { var dataItem = checkbox.up('dataitem'), rec = dataItem.getRecord(), recVal = rec.get('complete'); // this check is needed to prevent an issue where multiple creates get triggered from one create if (newVal !== recVal) { rec.set('complete', newVal); rec.save({ success: function (rec, operation) { rec.commit(); }, failure: function (rec, operation) { // since there was a failure doing the update on the server then silently reject the change rec.reject(true); Ext.Msg.alert( 'Error', Ext.util.Format.format('There was an error updating this task.

Status Code: {0}
Status Text: {1}', operation.error.status, operation.error.statusText) ); } }); } }, onItemEdit : function (list, index, target, record, e, eOpts) { var rec = list.getSelection()[0]; Ext.Msg.prompt('Edit', 'Rename task', function (buttonId, value) { if (buttonId === 'ok') { rec.set('text', value); rec.save({ success: function (rec, operation) { rec.commit(); }, failure: function (rec, operation) { // since there was a failure doing the update on the server then reject the change rec.reject(); Ext.Msg.alert( 'Error', Ext.util.Format.format('There was an error updating this task.

Status Code: {0}
Status Text: {1}', operation.error.status, operation.error.statusText) ); } }); } }, null, false, record.get('text') ); }, onReload : function () { Ext.getStore('TodoItems').load(); }, onAddItem : function () { var me = this, rec, store = Ext.getStore('TodoItems'), field = me.getTodoField(), value = field.getValue(); if (value === '') { Ext.Msg.alert('Error', 'Please enter Task name', Ext.emptyFn); } else { rec = Ext.create('Basic.model.TodoItem', { complete : false, text : value }); //store.insert(0, rec); //insert at the top //store.sync(); rec.save({ success: function (rec, operation) { store.insert(0, rec); //insert at the top field.setValue(''); }, failure: function (rec, operation) { Ext.Msg.alert( 'Error', Ext.util.Format.format('There was an error creating this task.

Status Code: {0}
Status Text: {1}', operation.error.status, operation.error.statusText) ); } }); } } }); ### Put it all together **Filename**: app.js Our final step is to finish editing the main application file, and provide information about the models, stores, views and controllers that have defined. The source files for these resources are automatically loaded into the application. Finally, the launch method is called which creates and displays the primary application view 'Basic.main.View'. Ext.Loader.setConfig({ enabled : true, paths : { 'Ext' : 'touch/src', 'Ext.azure' : 'packages/azure/src' } }); Ext.application({ name : 'Basic', requires : [ 'Ext.MessageBox', 'Ext.azure.Azure' ], views : [ 'Main' ], controllers : [ 'Main' ], stores : [ 'TodoItems' ], azure : { appUrl : 'YOUR_APP_URL.azure-mobile.net', appKey : 'YOUR_APP_KEY' }, icon : { '57' : 'resources/icons/Icon.png', '72' : 'resources/icons/Icon~ipad.png', '114' : 'resources/icons/Icon@2x.png', '144' : 'resources/icons/Icon~ipad@2x.png' }, isIconPrecomposed : true, startupImage : { '320x460' : 'resources/startup/320x460.jpg', '640x920' : 'resources/startup/640x920.png', '768x1004' : 'resources/startup/768x1004.png', '748x1024' : 'resources/startup/748x1024.png', '1536x2008' : 'resources/startup/1536x2008.png', '1496x2048' : 'resources/startup/1496x2048.png' }, launch : function () { // Destroy the #appLoadingIndicator element Ext.fly('appLoadingIndicator').destroy(); // Initialize Azure Ext.Azure.init(this.config.azure); // Initialize the main view Ext.Viewport.add(Ext.create('Basic.view.Main')); }, onUpdated : function () { Ext.Msg.confirm( "Application Update", "This application has just successfully been updated to the latest version. Reload now?", function (buttonId) { if (buttonId === 'yes') { window.location.reload(); } } ); } }); ### Host and run your Sencha Touch app The final stage of this tutorial is to host and run your new app on your local computer. 1. In your terminal, browse to the location of your unzipped application. 2. Using Sencha Cmd, run the following commands: * *sencha app refresh* : This will instruct Sencha Cmd to locate all app dependencies, and download any needed packages (for example, [Sencha Touch Extensions for Azure](https://market.sencha.com/extensions/sencha-extensions-for-microsoft-azure)). * *sencha web start* : This will start a local web server to test our application. ![sencha web start](./media/partner-sencha-mobile-services-get-started/sencha-web-start.png) 3. Open the URL listed in your terminal in a web browser to start the app (e.g. http://localhost:1841). 4. In the app, type meaningful text, such as "Complete the tutorial", and then click **Add**. ![new todo item](./media/partner-sencha-mobile-services-get-started/new-todo-item.png) This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. 5. Back in the [Azure classic portal], click the **Data** tab and then click the TodoItems table. ![Todo Items table](./media/partner-sencha-mobile-services-get-started/mobile-data-tab.png) This lets you browse the data inserted by the app into the table. ![browse todo table](./media/partner-sencha-mobile-services-get-started/mobile-data-browse.png) ## Next Steps Now that you have completed the Getting Started Guide, learn how to perform additional important tasks in Mobile Services with Sencha. [Download](https://github.com/arthurakay/sencha-touch-azure-example) a completed sample app with additional styling and features to see what else Sencha Touch can do! Then, dive into more information about the Sencha Touch Extensions for Azure: * Sample app [walkthrough](http://docs.sencha.com/touch-azure/1.0.0/#!/guide/data_filters) * Get help in the [Sencha Forums](http://www.sencha.com/forum) * Browse the [Sencha Documentation](http://docs.sencha.com/) * Using Sencha With Azure Mobile Services: [(Video)](http://channel9.msdn.com/Shows/Cloud+Cover/Episode-126-Using-Sencha-With-Windows-Azure-Mobile-Services) ## Additional Resources * [Download Sencha Touch](http://pages.sencha.com/touch-for-azure.html) * [Sencha Touch Extensions for Azure](https://market.sencha.com/extensions/sencha-extensions-for-microsoft-azure) ## Summary The example outlined here is provided in the Sencha Touch Extension for Azure package and is located in the examples directory as the Basic Data example. There are a few more examples which are provided which demonstrated other functionality of this extension along with detailed comments and explanations. For more information about getting started with Sencha Touch please visit the full set of [guides](http://docs.sencha.com/touch/#!/guide) [0]: ./media/partner-sencha-mobile-services-get-started/finished-app.png [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/partner-twilio-mobile-services-how-to-use-voice-sms.md ================================================ # How to use Twilio for voice and SMS capabilities from Mobile Services This topic shows you how to perform common tasks using the Twilio API with Azure Mobile Services. In this tutorial you will learn how to create Custom API scripts that use the Twilio API to initiate a phone call and to send a Short Message Service (SMS) message. ## What is Twilio? Twilio is powering the future of business communications, enabling developers to embed voice, VoIP, and messaging into applications. They virtualize all infrastructure needed in a cloud-based, global environment, exposing it through the Twilio communications API platform. Applications are simple to build and scalable. Enjoy flexibility with pay-as-you go pricing, and benefit from cloud reliability. **Twilio Voice** allows your applications to make and receive phone calls. **Twilio SMS** enables your applications to send and receive SMS messages. **Twilio Client** allows you to make VoIP calls from any phone, tablet, or browser and supports WebRTC. ## Twilio Pricing and Special Offers Azure customers receive a [special offer][special_offer]: complimentary $10 of Twilio Credit when you upgrade your Twilio Account. This Twilio Credit can be applied to any Twilio usage ($10 credit equivalent to sending as many as 1,000 SMS messages or receiving up to 1000 inbound Voice minutes, depending on the location of your phone number and message or call destination). Redeem this Twilio credit and get started at [ahoy.twilio.com/azure][special_offer]. Twilio is a pay-as-you-go service. There are no set-up fees and you can close your account at any time. You can find more details at [Twilio Pricing][twilio_pricing]. ## Concepts The Twilio API is a RESTful API that provides voice and SMS functionality for applications. Client libraries are available in multiple languages. Key aspects of the Twilio API are Twilio verbs and Twilio Markup Language (TwiML). ### Twilio verbs The API makes use of Twilio verbs; for example, the **<Say>** verb instructs Twilio to audibly deliver a message on a call. The following is a list of Twilio verbs. Learn about the other verbs and capabilities via [Twilio Markup Language documentation](http://www.twilio.com/docs/api/twiml). * **<Dial>**: Connects the caller to another phone. * **<Gather>**: Collects numeric digits entered on the telephone keypad. * **<Hangup>**: Ends a call. * **<Pause>**: Waits silently for a specified number of seconds. * **<Play>**: Plays an audio file. * **<Queue>**: Add the to a queue of callers. * **<Record>**: Records the caller's voice and returns a URL of a file that contains the recording. * **<Redirect>**: Transfers control of a call or SMS to the TwiML at a different URL. * **<Reject>**: Rejects an incoming call to your Twilio number without billing you * **<Say>**: Converts text to speech that is made on a call. * **<Sms>**: Sends an SMS message. ### TwiML TwiML is a set of XML-based instructions based on the Twilio verbs that inform Twilio of how to process a call or SMS. As an example, the following TwiML would convert the text **Hello World** to speech. Hello World When your application calls the Twilio API, one of the API parameters is the URL that returns the TwiML response. For development purposes, you can use Twilio-provided URLs to provide the TwiML responses used by your applications. You could also host your own URLs to produce the TwiML responses, and another option is to use the `TwiMLResponse` object. For more information about Twilio verbs, their attributes, and TwiML, see [TwiML][twiml]. For additional information about the Twilio API, see [Twilio API][twilio_api]. ## Create a Twilio Account When you're ready to get a Twilio account, sign up at [Try Twilio][try_twilio]. You can start with a free account, and upgrade your account later. When you sign up for a Twilio account, you'll receive an account SID and an authentication token. Both will be needed to make Twilio API calls. To prevent unauthorized access to your account, keep your authentication token secure. Your account SID and authentication token are viewable in the [Twilio Console][twilio_console], in the fields labeled **ACCOUNT SID** and **AUTH TOKEN**, respectively. ## Create a Mobile Service A Mobile Service that hosts a Twilio enabled application is no different from any other Mobile Service. You simply add the Twilio node.js library in order to reference it from your Mobile Service Custom API scripts. For information on creating an initial mobile service, see [Getting Started with Mobile Services](mobile-services-ios-get-started.md). ## Configure Your Mobile Service to use the Twilio Node.js Library Twilio provides a Node.js library that wraps various aspects of Twilio to provide simple and easy ways to interact with the Twilio REST API and Twilio Client to generate TwiML responses. To use the Twilio node.js library in your Mobile Service, you need leverage Mobile Services npm module support, which you can do by storing your scripts in source control. 1. Complete the tutorial [Store Scripts in Source Control](mobile-services-store-scripts-source-control.md). This walks you through setting-up source control for your Mobile Services and storing your server scripts in a Git repository. 2. After you have set up source control for your Mobile Service, open the repository on your local computer, browse to the `\services` subfolder, open the package.json file in a text editor, and add the following field to the **dependencies** object: "twilio": "~2.11.1" 3. After you have added the Twilio package reference to the **dependencies** object, the package.json file should look like the following: { "name": "todolist", "version": "1.0.0", "description": "todolist - hosted on Azure Mobile Services", "main": "server.js", "engines": { "node": ">= 0.8.19" }, "dependencies": { "twilio": "~2.11.1" }, "devDependencies": {}, "scripts": {}, "author": "unknown", "licenses": [], "keywords":[] } >[AZURE.NOTE]The dependency for Twilio should be added as `"twilio": "~2.11.1"`, with a (~). A reference with a caret (^) is not supported. 4. Commit this file update and push the update back to the mobile service. This update to the package.json file will restart your mobile service. The mobile service now installs and loads the Twilio package so you can reference and use the Twilio library in your custom API and table scripts. ## How to: Make an outgoing call The following script shows how to initiate an outgoing call from your Mobile Service using the **Call** resource. This code also uses a Twilio-provided site to return the Twilio Markup Language (TwiML) response. Substitute your values for the `from` and `to` phone numbers, and ensure that you verify the `from` phone number for your Twilio account before running the code. var twilio = require('twilio'); exports.post = function(request, response) { var client = new twilio.RestClient('ACCOUNT_SID', 'AUTH_TOKEN'); client.calls.create({ to:'+16515556677', from: '+14506667788', url: 'http://www.example.com/twiml.php' }, function(err, responseData) { console.log(responseData.from); response.send(200, ''); }); }; For more information about the parameters passed in to the `client.calls.create` function, see [http://www.twilio.com/docs/api/rest/making-calls][twilio_rest_making_calls]. As mentioned, this code uses a Twilio-provided site to return the TwiML response. You could instead use your own site to provide the TwiML response. For more information, see [How to: Provide TwiML responses from your own web site](#howto_provide_twiml_responses). ## How to: Send an SMS message The following code shows how to send an SMS message using the **Message** resource. The `from` number is provided by Twilio for trial accounts to send SMS messages. The `to` number must be verified for your Twilio account before you run the code. var twilio = require('twilio'); exports.post = function(request, response) { var client = new twilio.RestClient('ACCOUNT_SID', 'AUTH_TOKEN'); client.messages.create({ to:'+16515556677', from:'+14506667788', body:'ahoy hoy! Testing Twilio and node.js' }, function(error, message) { // The "error" variable will contain error information, if any. // If the request was successful, this value will be "false" if (!error) { console.log('Success! The SID for this SMS message is: ' + message.sid); console.log('Message sent on: ' + message.dateCreated); } else { console.log('Oops! There was an error.'); } response.send(200, { error : error } ); }); }; ## How to: Provide TwiML responses from your own website When your application initiates a call to the Twilio API, Twilio sends your request to a URL that is expected to return a TwiML response. The example in How to: Make an outgoing call uses the Twilio-provided URL http://twimlets.com/message to return the response. > [AZURE.NOTE] While TwiML is designed for use by web services, you can view the TwiML in your browser. For example, click [twimlet_message_url](http://twimlets.com/message) to see an empty <Response> element; as another example, click [twimlet_message_url_hello_world](http://twimlets.com/message?Message%5B0%5D=Hello%20World) to see a <Response> element that contains a <Say> element. Instead of relying on the Twilio-provided URL, you can create your own URL site that returns HTTP responses. You can create the site in any language that returns HTTP responses. This topic assumes you'll be hosting the URL from an Node.js generic handler. The following script results in a TwiML response that says Hello World on the call. var twilio = require('twilio'); exports.post = function(request, response) { var twiml = new twilio.TwimlResponse(); twiml.say('ahoy hoy! Testing Twilio and node.js', { voice: 'woman' }); response.set('Content-Type', 'text/xml'); response.send(twiml.toString()); }; For more information about TwiML, see [https://www.twilio.com/docs/api/twiml][twiml]. Once you have set up a way to provide TwiML responses, you can pass that URL into the `client.calls.create` method as shown in the following code sample: var twilio = require('twilio'); exports.post = function(request, response) { var client = new twilio.RestClient('ACCOUNT_SID', 'AUTH_TOKEN'); client.calls.create({ to:'+16515556677', from: '+14506667788', url: 'http://.azure-mobile.net/api/makeCall' }, function(err, responseData) { console.log(responseData.from); response.send(''); }); }; ## How to: Use additional Twilio services In addition to the examples shown here, Twilio offers web-based APIs that you can use to leverage additional Twilio functionality from your Azure application. For full details, see the [Twilio API documentation][twilio_api]. ## Next steps Now that you've learned the basics of the Twilio service, follow these links to learn more: * [Twilio Security Guidelines] [twilio_security_guidelines] * [Twilio HowTos and Example Code] [twilio_howtos] * [Twilio Quickstart Tutorials][twilio_quickstarts] * [Twilio on GitHub] [twilio_on_github] * [Talk to Twilio Support] [twilio_support] [twilio_security_guidelines]: http://www.twilio.com/docs/security [twilio_howtos]: http://www.twilio.com/docs/howto [twilio_on_github]: https://github.com/twilio [twilio_support]: http://www.twilio.com/help/contact [twilio_quickstarts]: http://www.twilio.com/docs/quickstart [twilio_rest_making_calls]: http://www.twilio.com/docs/api/rest/making-calls [twilio_pricing]: http://www.twilio.com/pricing [special_offer]: http://ahoy.twilio.com/azure [twiml]: http://www.twilio.com/docs/api/twiml [twilio_api]: http://www.twilio.com/api [try_twilio]: https://www.twilio.com/try-twilio [twilio_console]: https://www.twilio.com/console ================================================ FILE: docs/partner-xamarin-mobile-services-android-get-started-push.md ================================================ # Add push notifications to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Overview This topic shows you how to use Azure Mobile Services to send push notifications to a Xamarin.Android app. In this tutorial you add push notifications using the Google Cloud Messaging (GCM) service to the [Get started with Mobile Services] project. When complete, your mobile service will send a push notification each time a record is inserted. This tutorial requires the following: + An active Google account. + [Google Cloud Messaging Client Component]. You will add this component during the tutorial. You should already have Xamarin and the [Azure Mobile Services Component] installed in your project from when you completed either [Get started with Mobile Services]. ## Enable Google Cloud Messaging 1. Navigate to the [Google Cloud Console](https://console.developers.google.com/project), sign in with your Google account credentials. 2. Click **Create Project**, type a project name, then click **Create**. If requested, carry out the SMS Verification, and click **Create** again. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-new-project.png) Type in your new **Project name** and click **Create project**. 3. Click the **Utilities and More** button and then click **Project Information**. Make a note of the **Project Number**. You will need to set this value as the `SenderId` variable in the client app. ![](./media/mobile-services-enable-google-cloud-messaging/notification-hubs-utilities-and-more.png) 4. In the project dashboard, under **Mobile APIs**, click **Google Cloud Messaging**, then on the next page click **Enable API** and accept the terms of service. ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-GCM.png) ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-gcm-2.png) 5. In the project dashboard, Click **Credentials** > **Create Credential** > **API Key**. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-create-server-key.png) 6. In **Create a new key**, click **Server key**, type a name for your key, then click **Create**. 7. Make a note of the **API KEY** value. You will use this API key value to enable Azure to authenticate with GCM and send push notifications on behalf of your app. ## Configure your mobile service to send push requests 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your app. 2. Click the **Push** tab, enter the **API Key** value obtained from GCM in the previous procedure, then click **Save**. ![](./media/mobile-services-android-configure-push/mobile-push-tab-android.png) >[AZURE.NOTE]When you set your GCM credentials for enhanced push notifications in the Push tab in the portal, they are shared with Notification Hubs to configure the notification hub with your app. Both your mobile service and your app are now configured to work with GCM and Notification Hubs. ## Update the registered insert script to send notifications >[AZURE.TIP] The following steps show you how to update the script registered to the insert operation on the TodoItem table in the Azure classic portal. You can also access and edit this mobile service script directly in Visual Studio, in the Azure node of Server Explorer. 1. In the [Azure classic portal](https://manage.windowsazure.com/), click the **Data** tab and then click the **TodoItem** table. 2. In **todoitem**, click the **Script** tab and select **Insert**. This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Define a simple payload for a GCM notification. var payload = { "data": { "message": item.text } }; request.execute({ success: function() { // If the insert succeeds, send a notification. push.gcm.send(null, payload, { success: function(pushResponse) { console.log("Sent push:", pushResponse, payload); request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); request.respond(500, { error: pushResponse }); } }); }, error: function(err) { console.log("request.execute error", err) request.respond(); } }); } This registers a new insert script, which uses the [gcm object](http://go.microsoft.com/fwlink/p/?LinkId=282645) to send a push notification to all registered devices after the insert succeeds. ## Configure the existing project for push notifications 1. In the Solution view (or **Solution Explorer** in Visual Studio), right-click the **Components** folder, click **Get More Components...**, search for the **Google Cloud Messaging Client** component and add it to the project. 2. Open the ToDoActivity.cs project file and add the following using statement to the class: using Gcm.Client; 3. In the **ToDoActivity** class, add the following new code: // Create a new instance field for this activity. static ToDoActivity instance = new ToDoActivity(); // Return the current activity instance. public static ToDoActivity CurrentActivity { get { return instance; } } // Return the Mobile Services client. public MobileServiceClient CurrentClient { get { return client; } } This enables you to access the mobile client instance from the push handler service process. 4. Add the following code to the **OnCreate** method, after the **MobileServiceClient** is created: // Set the current instance of TodoActivity. instance = this; // Make sure the GCM client is set up correctly. GcmClient.CheckDevice(this); GcmClient.CheckManifest(this); // Register the app for push notifications. GcmClient.Register(this, ToDoBroadcastReceiver.senderIDs); Your **ToDoActivity** is now prepared for adding push notifications. ## Add push notifications code to your app 4. Create a new class in the project called `ToDoBroadcastReceiver`. 5. Add the following using statements to **ToDoBroadcastReceiver** class: using Gcm.Client; using Microsoft.WindowsAzure.MobileServices; 6. Add the following permission requests between the **using** statements and the **namespace** declaration: [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] //GET_ACCOUNTS is only needed for android versions 4.0.3 and below [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")] 7. Replace the existing **ToDoBroadcastReceiver** class definition with the following: [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })] public class ToDoBroadcastReceiver : GcmBroadcastReceiverBase { // Set the Google app ID. public static string[] senderIDs = new string[] { "" }; } In the above code, you must replace _``_ with the project number assigned by Google when you provisioned your app in the Google developer portal. 8. In the ToDoBroadcastReceiver.cs project file, add the following code that defines the **PushHandlerService** class: // The ServiceAttribute must be applied to the class. [Service] public class PushHandlerService : GcmServiceBase { public static string RegistrationID { get; private set; } public PushHandlerService() : base(ToDoBroadcastReceiver.senderIDs) { } } Note that this class derives from **GcmServiceBase** and that the **Service** attribute must be applied to this class. >[AZURE.NOTE]The **GcmServiceBase** class implements the **OnRegistered()**, **OnUnRegistered()**, **OnMessage()** and **OnError()** methods. You must override these methods in the **PushHandlerService** class. 5. Add the following code to the **PushHandlerService** class that overrides the **OnRegistered** event handler. protected override void OnRegistered(Context context, string registrationId) { System.Diagnostics.Debug.WriteLine("The device has been registered with GCM.", "Success!"); // Get the MobileServiceClient from the current activity instance. MobileServiceClient client = ToDoActivity.CurrentActivity.CurrentClient; var push = client.GetPush(); List tags = null; //// (Optional) Uncomment to add tags to the registration. //var tags = new List() { "myTag" }; // create tags if you want try { // Make sure we run the registration on the same thread as the activity, // to avoid threading errors. ToDoActivity.CurrentActivity.RunOnUiThread( async () => await push.RegisterNativeAsync(registrationId, tags)); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine( string.Format("Error with Azure push registration: {0}", ex.Message)); } } This method uses the returned GCM registration ID to register with Azure for push notifications. 10. Override the **OnMessage** method in **PushHandlerService** with the following code: protected override void OnMessage(Context context, Intent intent) { string message = string.Empty; // Extract the push notification message from the intent. if (intent.Extras.ContainsKey("message")) { message = intent.Extras.Get("message").ToString(); var title = "New item added:"; // Create a notification manager to send the notification. var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager; // Create a new intent to show the notification in the UI. PendingIntent contentIntent = PendingIntent.GetActivity(context, 0, new Intent(this, typeof(ToDoActivity)), 0); // Create the notification using the builder. var builder = new Notification.Builder(context); builder.SetAutoCancel(true); builder.SetContentTitle(title); builder.SetContentText(message); builder.SetSmallIcon(Resource.Drawable.ic_launcher); builder.SetContentIntent(contentIntent); var notification = builder.Build(); // Display the notification in the Notifications Area. notificationManager.Notify(1, notification); } } 12. Override the **OnUnRegistered()** and **OnError()** methods with the following code. protected override void OnUnRegistered(Context context, string registrationId) { throw new NotImplementedException(); } protected override void OnError(Context context, string errorId) { System.Diagnostics.Debug.WriteLine( string.Format("Error occurred in the notification: {0}.", errorId)); } ## Test push notifications in your app You can test the app by directly attaching an Android phone with a USB cable, or by using a virtual device in the emulator. 4. Create a new class in the project called `ToDoBroadcastReceiver`. 5. Add the following using statements to **ToDoBroadcastReceiver** class: using Gcm.Client; using Microsoft.WindowsAzure.MobileServices; 6. Add the following permission requests between the **using** statements and the **namespace** declaration: [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] //GET_ACCOUNTS is only needed for android versions 4.0.3 and below [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")] 7. Replace the existing **ToDoBroadcastReceiver** class definition with the following: [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })] public class ToDoBroadcastReceiver : GcmBroadcastReceiverBase { // Set the Google app ID. public static string[] senderIDs = new string[] { "" }; } In the above code, you must replace _``_ with the project number assigned by Google when you provisioned your app in the Google developer portal. 8. In the ToDoBroadcastReceiver.cs project file, add the following code that defines the **PushHandlerService** class: // The ServiceAttribute must be applied to the class. [Service] public class PushHandlerService : GcmServiceBase { public static string RegistrationID { get; private set; } ### Setting up the Android emulator for testing When you run this app in the emulator, make sure that you use an Android Virtual Device (AVD) that supports Google APIs. > [AZURE.IMPORTANT] In order to receive push notifications, you must set up a Google account on your Android Virtual Device (in the emulator, navigate to **Settings** and click **Add Account**). Also, make sure that the emulator is connected to the Internet. 1. From **Tools**, click **Open Android Emulator Manager**, select your device, and then click **Edit**. ![Android Virtual Device Manager](./media/mobile-services-android-push-notifications-test/notification-hub-create-android-app7.png) 2. Select **Google APIs** in **Target**, then click **OK**. ![Edit the Android Virtual Device](./media/mobile-services-android-push-notifications-test/notification-hub-create-android-app8.png) 3. On the top toolbar, click **Run**, and then select your app. This starts the emulator and runs the app. The app retrieves the *registrationId* from GCM and registers with the Notification Hub. ### Inserting a new item generates a notification. 1. In the app, type meaningful text, such as _A new Mobile Services task_ and then click the **Add** button. 2. Swipe down from the top of the screen to open the device's Notification Center to see the notification. ![View notification in the Notification Center](./media/mobile-services-android-push-notifications-test/notification-area-received.png) You have successfully completed this tutorial. ## Next steps Learn more about Mobile Services and Notification Hubs in the following topics: * [Get started with authentication](mobile-services-android-get-started-users.md)
Learn how to authenticate users of your app with different account types using mobile services. * [What are Notification Hubs?](https://azure.microsoft.com/en-us/documentation/articles/notification-hubs-push-notification-overview/)
Learn more about how Notification Hubs works to deliver notifications to your apps across all major client platforms. * [Debug Notification Hubs applications](http://go.microsoft.com/fwlink/p/?linkid=386630)
Get guidance troubleshooting and debugging Notification Hubs solutions. * [How to use the .NET client library for Mobile Services](mobile-services-dotnet-how-to-use-client-library.md)
Learn more about how to use Mobile Services with Xamarin C# code. * [Mobile Services server script reference](mobile-services-how-to-use-server-scripts.md)
Learn more about how to implement business logic in your mobile service. [Get started with Mobile Services]: mobile-services-ios-get-started.md [Google Cloud Messaging Client Component]: http://components.xamarin.com/view/GCMClient/ [Azure Mobile Services Component]: http://components.xamarin.com/view/azure-mobile-services/ ================================================ FILE: docs/partner-xamarin-mobile-services-android-get-started-users.md ================================================ # Add authentication to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).

This topic shows you how to authenticate users in Azure Mobile Services from your Xamarin.Android app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed.

This tutorial walks you through these basic steps to enable authentication in your app: 1. [Register your app for authentication and configure Mobile Services] 2. [Restrict table permissions to authenticated users] 3. [Add authentication to the app] This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. Completing this tutorial requires Visual Studio with Xamarin on Windows or Xamarin Studio on Mac OS X. Complete installation instructions are on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario. 3. In Xamarin Studio, open the project that you created when you completed the tutorial [Get started with Mobile Services]. 4. From the **Run** menu, click **Start debugging** to start the app; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the _TodoItem_ table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app 1. Add the following property to the **ToDoActivity** class: private MobileServiceUser user; 2. Add the following method to the **ToDoActivity** class: private async Task Authenticate() { try { user = await client.LoginAsync(this, MobileServiceAuthenticationProvider.MicrosoftAccount); CreateAndShowDialog(string.Format("you are now logged in - {0}", user.UserId), "Logged in!"); } catch (Exception ex) { CreateAndShowDialog(ex, "Authentication failed"); } } This creates a new method to handle the authentication process. The user is authenticated by using a Microsoft Account login. A dialog is displayed which displays the ID of the authenticated user. You cannot proceed without a positive authentication. > [AZURE.NOTE] If you are using an identity provider other than Microsoft, change the value passed to the **login** method above to one of the following: _Facebook_, _Google_, _Twitter_, or _WindowsAzureActiveDirectory_. 3. In the **OnCreate** method, add the following line of code after the code that instantiates the `MobileServiceClient` object. await Authenticate(); This call starts the authentication process and awaits it asynchronously. 4. Move the remaining code after `await Authenticate();` in the **OnCreate** method to a new **CreateTable** method, which looks like this: private async Task CreateTable() { await InitLocalStoreAsync(); // Get the Mobile Service Table instance to use toDoTable = client.GetSyncTable(); textNewToDo = FindViewById(Resource.Id.textNewToDo); // Create an adapter to bind the items with the view adapter = new ToDoItemAdapter(this, Resource.Layout.Row_List_To_Do); var listViewTodo = FindViewById(Resource.Id.listViewToDo); listViewTodo.Adapter = adapter; // Load the items from the Mobile Service await RefreshItemsFromTableAsync(); } 5. Then call the new **CreateTable** method in **OnCreate** after the **Authenticate** call added in step 2: await CreateTable(); 6. From the **Run** menu, click **Start debugging** to start the app and sign in with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query Mobile Services and make updates to data. ## Get completed example Download the [completed example project]. Be sure to update the **applicationURL** and **applicationKey** variables with your own Azure settings. ## Next steps In the next tutorial, [Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [4]: ./media/partner-xamarin-mobile-services-android-get-started-users/mobile-services-selection.png [5]: ./media/partner-xamarin-mobile-services-android-get-started-users/mobile-service-uri.png [13]: ./media/partner-xamarin-mobile-services-android-get-started-users/mobile-identity-tab.png [14]: ./media/partner-xamarin-mobile-services-android-get-started-users/mobile-portal-data-tables.png [15]: ./media/partner-xamarin-mobile-services-android-get-started-users/mobile-portal-change-table-perms.png [Get started with Mobile Services]: partner-xamarin-mobile-services-android-get-started.md [Authorize users with scripts]: mobile-services-javascript-backend-service-side-authorization.md [completed example project]: http://go.microsoft.com/fwlink/p/?LinkId=331328 ================================================ FILE: docs/partner-xamarin-mobile-services-android-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a Xamarin.Android app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple *To do list* app that stores app data in the new mobile service. If you prefer to watch a video, the clip below follows the same steps on this tutorial. Video: "Getting Started with Xamarin and Azure Mobile Services" with Craig Dunn, developer evangelist for Xamarin (duration: 10:05 min) > [AZURE.VIDEO getting-started-with-xamarin-and-mobile-services] A screenshot from the completed app is below: ![][0] Completing this tutorial requires XCode and Xamarin Studio for OS X or Visual Studio on Windows with a networked Mac. Complete installation instructions are on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). The downloaded quickstart project contains the Azure Mobile services component for Xamarin.Android. While this project targets Android 4.2 or a later version, the Mobile Services SDK requires only Android 2.2 or a later version. > [AZURE.IMPORTANT] To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/?WT.mc_id=A9C9624B5). ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Xamarin.Android app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will create a new Xamarin.Android app that is connected to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Xamarin.Android** under **Choose platform** and expand **Create a new Android app**. ![][6] This displays the three easy steps to create a Xamarin.Android app connected to your mobile service. ![][7] 3. Click **Create TodoItem table** to create a table to store app data. 4. Under **Download and run app**, click **Download**. This downloads the project for the sample _To do list_ application that is connected to your mobile service. Save the compressed project file to your local computer, and make a note of where you save it. ## Run your Android app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files and expand the files on your computer. 2. In Xamarin Studio or Visual Studio, click **File** then **Open**, navigate to the uncompressed sample files, and select **XamarinTodoQuickStart.Android.sln** to open it. 3. Press the **Run** button to build the project and start the app. You will be asked to select an emulator or a connected USB device. > [AZURE.NOTE] To be able to run the project in the Android emulator, you must define at least one Android Virtual Device (AVD). Use the AVD Manager to create and manage these devices. 4. In the app, type meaningful text, such as _Complete the tutorial_, and then click **Add**. ![][10] This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. > [AZURE.NOTE] > You can review the code that accesses your mobile service to query and insert data, which is found in the ToDoActivity.cs C# file. 6. Back in the [Azure classic portal], click the **Data** tab and then click the **TodoItems** table. ![][11] This lets you browse the data inserted by the app into the table. ![][12] ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync] Learn how the quickstart uses offline data sync to make the app responsive and robust. * [Get started with authentication] Learn how to authenticate users of your app with an identity provider. * [Get started with push notifications] Learn how to send a very basic push notification to your app. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [0]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-quickstart-completed-android.png [2]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-create.png [3]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-create-page1.png [4]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-create-page2.png [5]: ./media/partner-xamarin-mobile-services-android-get-started/obile-services-selection.png [6]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-portal-quickstart-xamarin-android.png [7]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-quickstart-steps-xamarin-android.png [8]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-xamarin-project-android-xs.png [9]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-xamarin-project-android-vs.png [10]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-quickstart-startup-android.png [11]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-data-tab.png [12]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-data-browse.png [13]: ./media/partner-xamarin-mobile-services-android-get-started/mobile-services-diagram.png [Get started with data]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-data-xamarin-android [Get started with offline data sync]: mobile-services-xamarin-android-get-started-offline-data.md [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-xamarin-android [Get started with push notifications]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-push-xamarin-android [Mobile Services Android SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Azure]: http://azure.microsoft.com/ [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/partner-xamarin-mobile-services-ios-get-started-push.md ================================================ # Add push notifications to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Overview This topic shows you how to use Azure Mobile Services to send push notifications to a Xamarin.iOS 8 app. In this tutorial you add push notifications using the Apple Push Notification service (APNS) to the [Get started with Mobile Services] project. When complete, your mobile service will send a push notification each time a record is inserted. This tutorial requires the following: + An iOS 8 device (you cannot test push notifications in the iOS Simulator) + iOS Developer Program membership + [Xamarin Studio] + [Azure Mobile Services Component] >[AZURE.IMPORTANT] Because of APNS requirements, you must deploy and test push notifications on an iOS capable device (iPhone or iPad) instead of in the emulator. APNS uses certificates to authenticate your mobile service. Follow these instructions to create the necessary certificates and upload it to your Mobile Service. For the official APNS feature documentation, see [Apple Push Notification Service]. ## Generate the Certificate Signing Request file First you must generate the Certificate Signing Request (CSR) file, which is used by Apple to generate a signed certificate. 1. From Utilities, run the **Keychain Access tool**. 2. Click **Keychain Access**, expand **Certificate Assistant**, then click **Request a Certificate from a Certificate Authority...**. ![][5] 3. Enter your **User Email Address**, type in a **Common Name** value, make sure that **Saved to disk** is selected, and then click **Continue**. ![][6] 4. Type a name for the Certificate Signing Request (CSR) file in **Save As**, select the location in **Where**, then click **Save**. ![][7] Remember the location you chose. Next, you will register your app with Apple, enable push notifications, and upload this exported CSR to create a push certificate. ## Register your app for push notifications To be able to send push notifications to an iOS app from mobile services, you must register your application with Apple and register for push notifications. 1. If you have not already registered your app, navigate to the iOS Provisioning Portal at the Apple Developer Center, log on with your Apple ID, click **Identifiers**, then click **App IDs**, and finally click on the **+** sign to create an app ID for your app. ![][102] 2. Type a name for your app in **Description**, enter and remember the unique **Bundle Identifier**, check the "Push Notifications" option in the "App Services" section, and then click **Continue**. This example uses the ID **MobileServices.Quickstart** but you may not reuse this same ID, as app IDs must be unique across all users. As such, it is recommended that you append your full name or initials after the app name. ![][103] This generates your app ID and requests you to **Submit** the information. Click **Submit**. ![][104] Once you click **Submit**, you will see the **Registration complete** screen, as shown below. Click **Done**. ![][105] 3. Locate the app ID that you just created, and click on its row. ![][106] Clicking on the app ID will display details on the app and app ID. Click the **Settings** button. ![][107] 4. Scroll to the bottom of the screen, and click the **Create Certificate...** button under the section **Development Push SSL Certificate**. ![][108] This displays the "Add iOS Certificate" assistant. Note: This tutorial uses a development certificate. The same process is used when registering a production certificate. Just make sure that you set the same certificate type when you upload the certificate to Mobile Services. 5. Click **Choose File**, browse to the location where you saved the CSR file earlier, then click **Generate**. ![][110] 6. After the certificate is created by the portal, click the **Download** button, and click **Done**. ![][111] This downloads the signing certificate and saves it to your computer in your Downloads folder. ![][9] Note: By default, the downloaded file a development certificate is named aps_development.cer. 7. Double-click the downloaded push certificate **aps_development.cer**. This installs the new certificate in the Keychain, as shown below: ![][10] Note: The name in your certificate might be different, but it will be prefixed with Apple Development iOS Push Notification Services:. Later, you will use this certificate to generate a .p12 file and upload it to Mobile Services to enable authentication with APNS. ## Create a provisioning profile for the app 1. Back in the iOS Provisioning Portal, select **Provisioning Profiles**, select **All**, and then click the **+** button to create a new profile. This launches the **Add iOS Provisiong Profile** Wizard. ![][112] 2. Select **iOS App Development** under **Development** as the provisiong profile type, and click **Continue**. 3. Next, select the app ID for the Mobile Services Quickstart app from the **App ID** drop-down list, and click **Continue**. ![][113] 4. In the **Select certificates** screen, select the certificate created earlier, and click **Continue**. ![][114] 5. Next, select the **Devices** to use for testing, and click **Continue**. ![][115] 6. Finally, pick a name for the profile in **Profile Name**, click **Generate**, and click **Done**. ![][116] This creates a new provisioning profile. ![][117] ## Configure Mobile Services to send push requests After you have registered your app with APNS and configured your project, you must next configure your mobile service to integrate with APNS. 1. In Keychain Access, right-click the new certificate, click **Export**, name your file, select the **.p12** format, then click **Save**. ![][28] Make a note of the file name and location of the exported certificate. 2. Log on to the [Azure classic portal], click **Mobile Services**, and then click your app. ![][18] 3. Click the **Push** tab and click **Upload** under **apple push notification settings**. ![][19] This displays the Upload Certificate dialog. 4. Click **File**, select the exported certificate .p12 file, enter the **Password**, make sure that the correct **Mode** is selected, click the check icon, then click **Save**. ![][20] Your mobile service is now configured to work with APNS. ## Configure your Xamarin.iOS application 1. In Xamarin.Studio, open **Info.plist**, and update the **Bundle Identifier** with the ID you created earlier. ![][121] 2. Scroll down to **Background Modes** and check the **Enable Background Modes** box and the **Remote notifications** box. ![][122] 3. Double click your project in the Solution Panel to open **Project Options**. 4. Choose **iOS Bundle Signing** under **Build**, and select the corresponding **Identity** and **Provisioning profile** you had just set up for this project. ![][120] This ensures that the Xamarin project uses the new profile for code signing. For the official Xamarin device provisioning documentation, see [Xamarin Device Provisioning]. ## Add push notifications to your app 1. In Xamarin.Studio, open the AppDelegate.cs file and add the following property: public string DeviceToken { get; set; } 2. Open the **TodoItem** class and add the following property: [JsonProperty(PropertyName = "deviceToken")] public string DeviceToken { get; set; } 3. In **QSTodoService**, override the existing client declaration to be: public MobileServiceClient client { get; private set; } 4. Then add the following method so **AppDelegate** can acquire the client later to register push notifications: public MobileServiceClient GetClient { get{ return client; } } 5. In **AppDelegate**, override the **FinishedLaunching** event: public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { // registers for push for iOS8 var settings = UIUserNotificationSettings.GetSettingsForTypes( UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet()); UIApplication.SharedApplication.RegisterUserNotificationSettings(settings); UIApplication.SharedApplication.RegisterForRemoteNotifications(); return true; } 6. In **AppDelegate**, override the **RegisteredForRemoteNotifications** event: public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) { // Modify device token DeviceToken = deviceToken.Description; DeviceToken = DeviceToken.Trim ('<', '>').Replace (" ", ""); // Get Mobile Services client MobileServiceClient client = QSTodoService.DefaultService.GetClient; // Register for push with Mobile Services IEnumerable tag = new List() { "uniqueTag" }; var push = client.GetPush (); push.RegisterNativeAsync (DeviceToken, tag); } 7. In **AppDelegate**, override the **ReceivedRemoteNotification** event: public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) { Debug.WriteLine(userInfo.ToString()); NSObject inAppMessage; bool success = userInfo.TryGetValue(new NSString("inAppMessage"), out inAppMessage); if (success) { var alert = new UIAlertView("Got push notification", inAppMessage.ToString(), null, "OK", null); alert.Show(); } } 8. In **QSTodoListViewController**, modify the **OnAdd** action to get the device token stored in **AppDelegeate**, and store it into the **TodoItem** being added. string deviceToken = ((AppDelegate)UIApplication.SharedApplication.Delegate).DeviceToken; var newItem = new TodoItem() { Text = itemText.Text, Complete = false, DeviceToken = deviceToken }; Your app is now updated to support push notifications. ## Update the registered insert script in the Azure classic portal 1. In the [Azure classic portal], click the **Data** tab and then click the **TodoItem** table. ![][21] 2. In **todoitem**, click the **Script** tab and select **Insert**. ![][22] This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { request.execute(); // Set timeout to delay the notification, to provide time for the // app to be closed on the device to demonstrate toast notifications setTimeout(function() { push.apns.send("uniqueTag", { alert: "Toast: " + item.text, payload: { inAppMessage: "Hey, a new item arrived: '" + item.text + "'" } }); }, 2500); } This registers a new insert script, which uses the [apns object] to send a push notification (the inserted text) to the device provided in the insert request. >[AZURE.NOTE] This script delays sending the notification to give you time to close the app to receive a toast notification. ## Test push notifications in your app 1. Press the **Run** button to build the project and start the app in an iOS capable device, then click **OK** to accept push notifications ![][23] >[AZURE.NOTE] You must explicitly accept push notifications from your app. This request only occurs the first time that the app runs. 2. In the app, type meaningful text, such as _A new Mobile Services task_ and then click the plus (**+**) icon. ![][24] 3. Verify that a notification is received, then click **OK** to dismiss the notification. ![][25] 4. Repeat step 2 and immediately close the app, then verify that the following toast is shown. ![][26] You have successfully completed this tutorial. [Generate the certificate signing request]: #certificates [Register your app and enable push notifications]: #register [Create a provisioning profile for the app]: #profile [Configure Mobile Services]: #configure-mobileServices [Configure the Xamarin.iOS App]: #configure-app [Update scripts to send push notifications]: #update-scripts [Add push notifications to the app]: #add-push [Insert data to receive notifications]: #test [5]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step5.png [6]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step6.png [7]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step7.png [9]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step9.png [10]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step10.png [17]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step17.png [18]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-selection.png [19]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-push-tab-ios.png [20]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-push-tab-ios-upload.png [21]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-portal-data-tables.png [22]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-insert-script-push2.png [23]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-quickstart-push1-ios.png [24]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-quickstart-push2-ios.png [25]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-quickstart-push3-ios.png [26]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-quickstart-push4-ios.png [28]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-step18.png [101]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-01.png [102]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-02.png [103]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-03.png [104]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-04.png [105]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-05.png [106]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-06.png [107]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-07.png [108]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-08.png [110]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-10.png [111]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-11.png [112]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-12.png [113]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-13.png [114]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-14.png [115]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-15.png [116]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-16.png [117]: ./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-17.png [120]:./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-20.png [121]:./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-21.png [122]:./media/partner-xamarin-mobile-services-ios-get-started-push/mobile-services-ios-push-22.png [Install Xcode]: https://go.microsoft.com/fwLink/p/?LinkID=266532 [iOS Provisioning Portal]: http://go.microsoft.com/fwlink/p/?LinkId=272456 [Mobile Services iOS SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Apple Push Notification Service]: http://go.microsoft.com/fwlink/p/?LinkId=272584 [Get started with Mobile Services]: mobile-services-ios-get-started.md [Xamarin Device Provisioning]: http://developer.xamarin.com/guides/ios/getting_started/installation/device_provisioning/ [Azure classic portal]: https://manage.windowsazure.com/ [apns object]: http://go.microsoft.com/fwlink/p/?LinkId=272333 [Azure Mobile Services Component]: http://components.xamarin.com/view/azure-mobile-services/ [completed example project]: http://go.microsoft.com/fwlink/p/?LinkId=331303 [Xamarin Studio]: http://xamarin.com/download ================================================ FILE: docs/partner-xamarin-mobile-services-ios-get-started-users.md ================================================ # Add authentication to your Mobile Services app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-users.md) - [(iOS | JavaScript)](mobile-services-ios-get-started-users.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-users.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-windows-phone-get-started-users.md) - [(Android | Javascript)](mobile-services-android-get-started-users.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started-users.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-users.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-users.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-users.md) - [(HTML | Javascript)](mobile-services-html-get-started-users.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This topic shows you how to authenticate users in Azure Mobile Services from your app. In this tutorial, you add authentication to the quickstart project using an identity provider that is supported by Mobile Services. After being successfully authenticated and authorized by Mobile Services, the user ID value is displayed. This tutorial walks you through these basic steps to enable authentication in your app: 1. [Register your app for authentication and configure Mobile Services] 2. [Restrict table permissions to authenticated users] 3. [Add authentication to the app] This tutorial is based on the Mobile Services quickstart. You must also first complete the tutorial [Get started with Mobile Services]. Completing this tutorial requires [Xamarin Studio], XCode 6.0, and iOS 7.0 or later versions. ## Register your app for authentication and configure Mobile Services 1. In the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Dashboard**, and make a note of the **Mobile Service URL** value. 2. Register your app with one or more of the following authentication providers: * [Google](./ mobile-services-how-to-register-google-authentication.md) * [Facebook](./ mobile-services-how-to-register-facebook-authentication.md) * [Twitter](./ mobile-services-how-to-register-twitter-authentication.md) * [Microsoft](./ mobile-services-how-to-register-microsoft-authentication.md) * [Azure Active Directory](./ mobile-services-how-to-register-active-directory-authentication.md). Make a note of the client identity and client secret values generated by the provider. Do not distribute or share the client secret. 3. Back in the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services** > your mobile service > **Identity** > your identity provider settings, then enter the client ID and secret value from your provider. You've now configured both your app and your mobile service to work with your auth provider. You may optionally repeat all these steps for each additional identity provider you'd like to support. > [AZURE.IMPORTANT] Verify that you've set the correct redirect URI on your identity provider's developer site. As described in the linked instructions for each provider above, the redirect URI may be different for a .NET backend service vs. for a JavaScript backend service. An incorrectly configured redirect URI may result in the login screen not being displayed properly and the app malfunctioning in unexpected ways. ## Restrict permissions to authenticated users To secure your endpoints, you must restrict access to only authenticated clients. 1. In the [Azure classic portal](https://manage.windowsazure.com/), navigate to your mobile service, then click **Data** > your table name (**TodoItem**) > **Permissions**. 2. Set all of the table operation permissions to **Only authenticated users**. This ensures that all operations against the table require an authenticated user, which is required for this tutorial. You can set different permissions on each operations to support your specific scenario. 3. In Xcode, open the project that you created when you completed the tutorial [Get started with Mobile Services]. 4. Press the **Run** button to build the project and start the app in the iPhone emulator; verify that an unhandled exception with a status code of 401 (Unauthorized) is raised after the app starts. This happens because the app attempts to access Mobile Services as an unauthenticated user, but the _TodoItem_ table now requires authentication. Next, you will update the app to authenticate users before requesting resources from the mobile service. ## Add authentication to the app 1. Open the **QSToDoService** project file and add the following variables // Mobile Service logged in user private MobileServiceUser user; public MobileServiceUser User { get { return user; } } 2. Then add a new method named **Authenticate** to **ToDoService** defined as: private async Task Authenticate(MonoTouch.UIKit.UIViewController view) { try { user = await client.LoginAsync(view, MobileServiceAuthenticationProvider.MicrosoftAccount); } catch (Exception ex) { Console.Error.WriteLine (@"ERROR - AUTHENTICATION FAILED {0}", ex.Message); } } > [AZURE.NOTE] If you are using an identity provider other than a Microsoft Account, change the value passed to **LoginAsync** above to one of the following: _Facebook_, _Twitter_, _Google_, or _WindowsAzureActiveDirectory_. 3. Move the request for the **ToDoItem** table from the **ToDoService** constructor into a new method named **CreateTable**: private async Task CreateTable() { // Create an MSTable instance to allow us to work with the ToDoItem table todoTable = client.GetSyncTable(); } 4. Create a new asynchronous public method named **LoginAndGetData** defined as: public async Task LoginAndGetData(MonoTouch.UIKit.UIViewController view) { await Authenticate(view); await CreateTable(); } 5. In the **TodoListViewController** override the **ViewDidAppear** method and define it as found below. This logs in the user if the **ToDoService** doesn't yet have a handle on the user: public override async void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); if (QSTodoService.DefaultService.User == null) { await QSTodoService.DefaultService.LoginAndGetData(this); } if (QSTodoService.DefaultService.User == null) { // TODO:: show error return; } await RefreshAsync(); } 6. Remove the original call to **RefreshAsync** from **TodoListViewController.ViewDidLoad**. 7. Press the **Run** button to build the project, start the app in the iPhone emulator, then log-on with your chosen identity provider. When you are successfully logged-in, the app should run without errors, and you should be able to query Mobile Services and make updates to data. ## Get completed example Download the [completed example project]. Be sure to update the **applicationURL** and **applicationKey** variables with your own Azure settings. ## Next steps In the next tutorial, [Authorize users with scripts], you will take the user ID value provided by Mobile Services based on an authenticated user and use it to filter the data returned by Mobile Services. [Register your app for authentication and configure Mobile Services]: #register [Restrict table permissions to authenticated users]: #permissions [Add authentication to the app]: #add-authentication [Next Steps]:#next-steps [4]: ./media/partner-xamarin-mobile-services-ios-get-started-users/mobile-services-selection.png [5]: ./media/partner-xamarin-mobile-services-ios-get-started-users/mobile-service-uri.png [13]: ./media/partner-xamarin-mobile-services-ios-get-started-users/mobile-identity-tab.png [14]: ./media/partner-xamarin-mobile-services-ios-get-started-users/mobile-portal-data-tables.png [15]: ./media/partner-xamarin-mobile-services-ios-get-started-users/mobile-portal-change-table-perms.png [Submit an app page]: http://go.microsoft.com/fwlink/p/?LinkID=266582 [My Applications]: http://go.microsoft.com/fwlink/p/?LinkId=262039 [Live SDK for Windows]: http://go.microsoft.com/fwlink/p/?LinkId=262253 [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-xamarin-ios [Get started with data]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-data-xamarin-ios [Get started with authentication]: https://azure.microsoft.com/develop/mobile/tutorials/get-started-with-users-xamarin-ios [Get started with push notifications]: https://azure.microsoft.com/develop/mobile/tutorials/-get-started-with-push-xamarin-ios [Authorize users with scripts]: https://azure.microsoft.com/develop/mobile/tutorials/authorize-users-in-scripts-xamarin-ios [completed example project]: http://go.microsoft.com/fwlink/p/?LinkId=331328 [Xamarin Studio]: http://xamarin.com/download ================================================ FILE: docs/partner-xamarin-mobile-services-ios-get-started.md ================================================ # Get started with Mobile Services > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started.md) - [(iOS | JavaScript)](mobile-services-ios-get-started.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-store-dotnet-get-started.md) - [(Windows Runtime 8.1 universal JavaScript | Javascript)](mobile-services-javascript-backend-windows-store-javascript-get-started.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started.md) - [(Android | Javascript)](mobile-services-android-get-started.md) - [(Xamarin.iOS | .NET)](mobile-services-dotnet-backend-xamarin-ios-get-started.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started.md) - [(HTML | Javascript)](mobile-services-html-get-started.md) - [(PhoneGap | Javascript)](mobile-services-javascript-backend-phonegap-get-started.md) - [(Sencha | Javascript)](partner-sencha-mobile-services-get-started.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). This tutorial shows you how to add a cloud-based backend service to a Xamarin.iOS app using Azure Mobile Services. In this tutorial, you will create both a new mobile service and a simple *To do list* app that stores app data in the new mobile service. If you prefer to watch a video, the clip below follows the same steps as this tutorial. Video: "Getting Started with Xamarin and Azure Mobile Services" with Craig Dunn, developer evangelist for Xamarin (duration: 10:05 min) > [AZURE.VIDEO getting-started-with-xamarin-and-mobile-services] A screenshot from the completed app is below: ![][0] Completing this tutorial requires XCode and Xamarin Studio for OS X or Visual Studio on Windows with a networked Mac. Complete installation instructions are on [Setup and Install for Visual Studio and Xamarin](https://msdn.microsoft.com/library/mt613162.aspx). > [AZURE.IMPORTANT] To complete this tutorial, you need an Azure account. If you don't have an account, you can sign up for an Azure trial and get up to 10 free mobile services that you can keep using even after your trial ends. For details, see [Azure Free Trial](https://azure.microsoft.com/pricing/free-trial/). ## Create a new mobile service Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). At the bottom of the navigation pane, click **+NEW**. Expand **Compute** and **Mobile Service**, then click **Create**. ![](./media/mobile-services-create-new-service/mobile-create.png) This displays the **Create a Mobile Service** dialog. 2. In the **Create a Mobile Service** dialog, select **Create a free 20 MB SQL Database**, select **JavaScript** runtime, then type a subdomain name for the new mobile service in the **URL** textbox. Click the right arrow button to go to the next page. ![](./media/mobile-services-create-new-service/mobile-create-page1.png) This displays the **Specify database settings** page. >[AZURE.NOTE]As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 3. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![](./media/mobile-services-create-new-service/mobile-create-page2.png) You have now created a new mobile service that can be used by your mobile apps. ## Create a new Xamarin.iOS app Once you have created your mobile service, you can follow an easy quickstart in the Azure classic portal to either create a new app or modify an existing app to connect to your mobile service. In this section you will create a new Xamarin.iOS app that is connected to your mobile service. 1. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service that you just created. 2. In the quickstart tab, click **Xamarin.iOS** under **Choose platform** and expand **Create a new Xamarin.iOS app**. ![][6] This displays the three easy steps to create a Xamarin.iOS app connected to your mobile service. ![][7] 3. If you haven't already done so, download and install Xcode (we recommend the latest version, Xcode 6.0, or newer) and [Xamarin Studio]. 4. Click **Create TodoItems table** to create a table to store app data. 5. Under **Download and run app**, click **Download**. This downloads the project for the sample _To do list_ application that is connected to your mobile service and references the Azure Mobile Services component for Xamarin.iOS. Save the compressed project file to your local computer, and make a note of where you saved it. ## Run your new Xamarin.iOS app The final stage of this tutorial is to build and run your new app. 1. Browse to the location where you saved the compressed project files, expand the files on your computer, and open the **XamarinTodoQuickStart.iOS.sln** solution file using Xamarin Studio or Visual Studio. ![][8] ![][9] 2. Press the **Run** button to build the project and start the app in the iPhone emulator, which is the default for this project. 3. In the app, type meaningful text, such as _Complete the tutorial_ and then click the plus (**+**) icon. ![][10] This sends a POST request to the new mobile service hosted in Azure. Data from the request is inserted into the TodoItem table. Items stored in the table are returned by the mobile service, and the data is displayed in the list. > [AZURE.NOTE] You can review the code that accesses your mobile service to query and insert data, which is found in the TodoService.cs C# file. 4. Back in the [Azure classic portal], click the **Data** tab and then click the **TodoItems** table. ![][11] This lets you browse the data inserted by the app into the table. ![][12] ## Next Steps Now that you have completed the quickstart, learn how to perform additional important tasks in Mobile Services: * [Get started with offline data sync] Learn how the quickstart uses offline data sync to make the app responsive and robust. * [Get started with authentication] Learn how to authenticate users of your app with an identity provider. * [Get started with push notifications] Learn how to send a very basic push notification to your app. [Getting started with Mobile Services]:#getting-started [Create a new mobile service]:#create-new-service [Define the mobile service instance]:#define-mobile-service-instance [Next Steps]:#next-steps [0]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-quickstart-completed-ios.png [6]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-portal-quickstart-xamarin-ios.png [7]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-quickstart-steps-xamarin-ios.png [8]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-xamarin-project-ios-xs.png [9]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-xamarin-project-ios-vs.png [10]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-quickstart-startup-ios.png [11]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-data-tab.png [12]: ./media/partner-xamarin-mobile-services-ios-get-started/mobile-data-browse.png [Get started with offline data sync]: mobile-services-xamarin-ios-get-started-offline-data.md [Get started with authentication]: partner-xamarin-mobile-services-ios-get-started-users.md [Get started with push notifications]: partner-xamarin-mobile-services-ios-get-started-push.md [Xamarin Studio]: http://xamarin.com/download [Mobile Services iOS SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Azure classic portal]: https://manage.windowsazure.com/ ================================================ FILE: docs/partner-xamarin-mobile-services-xamarin-forms-get-started-push.md ================================================ # Add push notifications to your Xamarin.Forms app > [AZURE.SELECTOR-LIST (Platform | Backend )] - [(iOS | .NET)](mobile-services-dotnet-backend-ios-get-started-push.md) - [(iOS | JavaScript)](mobile-services-javascript-backend-ios-get-started-push.md) - [(Windows Runtime 8.1 universal C# | .NET)](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Runtime 8.1 universal C# | Javascript)](mobile-services-javascript-backend-windows-universal-dotnet-get-started-push.md) - [(Windows Phone Silverlight 8.x | Javascript)](mobile-services-javascript-backend-windows-phone-get-started-push.md) - [(Android | .NET)](mobile-services-dotnet-backend-android-get-started-push.md) - [(Android | Javascript)](mobile-services-javascript-backend-android-get-started-push.md) - [(Xamarin.iOS | Javascript)](partner-xamarin-mobile-services-ios-get-started-push.md) - [(Xamarin.Android | Javascript)](partner-xamarin-mobile-services-android-get-started-push.md) - [(Xamarin.Android | .NET)](mobile-services-dotnet-backend-xamarin-android-get-started-push.md) - [(Xamarin.Forms | JavaScript)](partner-xamarin-mobile-services-xamarin-forms-get-started-push.md)   >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## Overview This tutorial shows you how to use Azure Mobile Services to send push notifications to the iOS, Android, and Windows Phone app of your Xamarin.Forms solution. Start by creating a mobile service. Then, you'll download a starter sample, register with the appropriate push notification services, and add code to the solution to receive notifications from those services. When you complete this tutorial, your mobile service will send a push notification each time a user adds a task in one of the apps. You can find the completed sample here: [Completed Xamarin.Forms Azure Push Notification Sample]. This tutorial requires the following: + An iOS 8 device (you cannot test push notifications in the iOS Simulator) + iOS Developer Program membership + [Xamarin Studio] + [Azure Mobile Services Component] + An active Google account + [Google Cloud Messaging Client Component]. You will add this component during the tutorial. In this topic: 1. [Create a New Mobile Service](#create-service) 2. [Download and Configure the Starter Sample](#download-starter-sample) 4. [Add push notifications to your Xamarin.Forms.iOS app](#iOS) 5. [Add push notifications to your Xamarin.Forms.Android app](#Android) 6. [Add push notifications to your Xamarin.Forms.Windows app](#Windows) 7. [Update Azure table insert script to send push notifications to all apps](#all-apps) ## Create a New Mobile Service Next, you will create a new mobile service to replace the in-memory list for data storage. Follow these steps to create a new mobile service. 1. Log into the [Azure classic portal](https://manage.windowsazure.com/). 2. At the bottom of the navigation pane, click **+NEW**. ![plus-new](./media/mobile-services-create-new-service-data/plus-new.png) 3. Expand **Compute** and **Mobile Service**, then click **Create**. ![mobile-create](./media/mobile-services-create-new-service-data/mobile-create.png) This displays the **New Mobile Service** dialog. 4. In the **Create a mobile service** page, select **Create a free 20 MB SQL Database**, then type a subdomain name for the new mobile service in the **URL** textbox and wait for name verification. Once name verification completes, click the right arrow button to go to the next page. ![mobile-create-page1](./media/mobile-services-create-new-service-data/mobile-create-page1.png) This displays the **Specify database settings** page. > [AZURE.NOTE] As part of this tutorial, you create a new SQL Database instance and server. You can reuse this new database and administer it as you would any other SQL Database instance. If you already have a database in the same region as the new mobile service, you can instead choose **Use existing Database** and then select that database. The use of a database in a different region is not recommended because of additional bandwidth costs and higher latencies. 5. In **Name**, type the name of the new database, then type **Login name**, which is the administrator login name for the new SQL Database server, type and confirm the password, and click the check button to complete the process. ![mobile-create-page2](./media/mobile-services-create-new-service-data/mobile-create-page2.png) > [AZURE.NOTE] When the password that you supply does not meet the minimum requirements or when there is a mismatch, a warning is displayed. > > We recommend that you make a note of the administrator login name and password that you specify; you will need this information to reuse the SQL Database instance or the server in the future. You have now created a new mobile service that can be used by your mobile apps. Next, you will add a new table in which to store app data. This table will be used by the app in place of the in-memory collection. To be able to store app data in the new mobile service, you must first create a new table. 1. In the Azure classic portal, click **Mobile Services**, and then click the mobile service that you just created. 2. Click the **Data** tab, then click **+Create**. ![][123] This displays the **Create new table** dialog. 3. In **Table name** type _TodoItem_, then click the check button. ![][124] This creates a new storage table **TodoItem** with the default permissions set, which means that any user of the app can access and change data in the table. > [AZURE.NOTE] The same table name is used in Mobile Services quickstart. However, each table is created in a schema that is specific to a given mobile service. This is to prevent data collisions when multiple mobile services use the same database. 4. Click the new **TodoItem** table and verify that there are no data rows. 5. Click the **Columns** tab and verify that there is only a single **id** column, which is automatically created for you. This is the minimum requirement for a table in Mobile Services. > [AZURE.NOTE] When dynamic schema is enabled on your mobile service, new columns are created automatically when JSON objects are sent to the mobile service by an insert or update operation. You are now ready to use the new mobile service as data storage for the app. ## Download and Configure the Starter Sample We'll add push notifications to an existing sample. 1. Download the following sample: [Xamarin.Forms Azure Push Notification Starter Sample]. 2. In the [Azure classic portal], click **Mobile Services**, and then click the mobile service. Click the **Dashboard** tab and make a note of the **Site URL**. Then click **Manage Keys** and make a note of the **Application Key**. You'll need these values when you access the mobile service from your app code. 3. In the **ToDoAzure(Portable)** project of the solution, open the **Constants.cs** file, replace `ApplicationURL` and `ApplicationKey` with the site URL and application key you obtained in the previous step. ## Add push notifications to your Xamarin.Forms.iOS app You'll add push notifications to the iOS app by using the Apple Push Notification service (APNS). You'll need an active Google account, and the [Google Cloud Messaging Client Component]. >[AZURE.IMPORTANT] Because of Apple Push Notification service (APNS) requirements, you must deploy and test push notifications on an iOS capable device (iPhone or iPad) instead of in the emulator. APNS uses certificates to authenticate your mobile service. Follow these instructions to create the necessary certificates and upload it to your Mobile Service. For the official APNS feature documentation, see [Apple Push Notification Service]. ### Generate the Certificate Signing Request file First you must generate the Certificate Signing Request (CSR) file, which is used by Apple to generate a signed certificate. 1. From Utilities, run the **Keychain Access tool**. 2. Click **Keychain Access**, expand **Certificate Assistant**, then click **Request a Certificate from a Certificate Authority...**. ![][5] 3. Enter your **User Email Address**, type in a **Common Name** value, make sure that **Saved to disk** is selected, and then click **Continue**. ![][6] 4. Type a name for the Certificate Signing Request (CSR) file in **Save As**, select the location in **Where**, then click **Save**. ![][7] Remember the location you chose. Next, you will register your app with Apple, enable push notifications, and upload this exported CSR to create a push certificate. ### Register your app for push notifications To be able to send push notifications to an iOS app from mobile services, you must register your application with Apple and register for push notifications. 1. If you have not already registered your app, navigate to the iOS Provisioning Portal at the Apple Developer Center, log on with your Apple ID, click **Identifiers**, then click **App IDs**, and finally click on the **+** sign to create an app ID for your app. ![][102] 2. Type a name for your app in **Description**, enter and remember the unique **Bundle Identifier**, check the "Push Notifications" option in the "App Services" section, and then click **Continue**. This example uses the ID **MobileServices.Quickstart** but you may not reuse this same ID, as app IDs must be unique across all users. As such, it is recommended that you append your full name or initials after the app name. ![][103] This generates your app ID and requests you to **Submit** the information. Click **Submit**. ![][104] Once you click **Submit**, you will see the **Registration complete** screen, as shown below. Click **Done**. ![][105] 3. Locate the app ID that you just created, and click on its row. ![][106] Clicking on the app ID will display details on the app and app ID. Click the **Settings** button. ![][107] 4. Scroll to the bottom of the screen, and click the **Create Certificate...** button under the section **Development Push SSL Certificate**. ![][108] This displays the "Add iOS Certificate" assistant. Note: This tutorial uses a development certificate. The same process is used when registering a production certificate. Just make sure that you set the same certificate type when you upload the certificate to Mobile Services. 5. Click **Choose File**, browse to the location where you saved the CSR file earlier, then click **Generate**. ![][110] 6. After the certificate is created by the portal, click the **Download** button, and click **Done**. ![][111] This downloads the signing certificate and saves it to your computer in your Downloads folder. ![][9] Note: By default, the downloaded file a development certificate is named aps_development.cer. 7. Double-click the downloaded push certificate **aps_development.cer**. This installs the new certificate in the Keychain, as shown below: ![][10] Note: The name in your certificate might be different, but it will be prefixed with Apple Development iOS Push Notification Services:. Later, you will use this certificate to generate a .p12 file and upload it to Mobile Services to enable authentication with APNS. ### Create a provisioning profile for the app 1. Back in the iOS Provisioning Portal, select **Provisioning Profiles**, select **All**, and then click the **+** button to create a new profile. This launches the **Add iOS Provisiong Profile** Wizard. ![][112] 2. Select **iOS App Development** under **Development** as the provisiong profile type, and click **Continue**. 3. Next, select the app ID for the Mobile Services Quickstart app from the **App ID** drop-down list, and click **Continue**. ![][113] 4. In the **Select certificates** screen, select the certificate created earlier, and click **Continue**. ![][114] 5. Next, select the **Devices** to use for testing, and click **Continue**. ![][115] 6. Finally, pick a name for the profile in **Profile Name**, click **Generate**, and click **Done**. ![][116] This creates a new provisioning profile. ![][117] 7. In Xcode, open the Organizer select the Devices view, select **Provisioning Profiles** in the **Library** section in the left pane, and then click the **Refresh** button at the bottom of the middle pane. ### Configure Mobile Services to send push requests After you have registered your app with APNS and configured your project, you must next configure your mobile service to integrate with APNS. 1. In Keychain Access, right-click the new certificate, click **Export**, name your file, select the **.p12** format, then click **Save**. ![][28] Make a note of the file name and location of the exported certificate. 2. Log on to the [Azure classic portal], click **Mobile Services**, and then click your app. ![][18] 3. Click the **Push** tab and click **Upload** under **apple push notification settings**. ![][19] This displays the Upload Certificate dialog. 4. Click **File**, select the exported certificate .p12 file, enter the **Password**, make sure that the correct **Mode** is selected, click the check icon, then click **Save**. ![][20] Your mobile service is now configured to work with APNS. ### Configure your Xamarin.iOS application 1. In Xamarin.Studio or Visual Studio, open **Info.plist**, and update the **Bundle Identifier** with the ID you created earlier. ![][121] 2. Scroll down to **Background Modes** and check the **Enable Background Modes** box and the **Remote notifications** box. ![][122] 3. Double click your project in the Solution Panel to open **Project Options**. 4. Choose **iOS Bundle Signing** under **Build**, and select the corresponding **Identity** and **Provisioning profile** you had just set up for this project. ![][120] This ensures that the Xamarin project uses the new profile for code signing. For the official Xamarin device provisioning documentation, see [Xamarin Device Provisioning]. ### Add push notifications to your app 1. In Xamarin.Studio or Visual Studio, expand the **ToDoAzure.iOS** project, open the **AppDelegate** class, and then replace the **FinishedLaunching** event with the following code: public override bool FinishedLaunching(UIApplication app, NSDictionary options) { // registers for push for iOS8 var settings = UIUserNotificationSettings.GetSettingsForTypes( UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet()); global::Xamarin.Forms.Forms.Init(); instance = this; CurrentPlatform.Init(); todoItemManager = new ToDoItemManager(); App.SetTodoItemManager(todoItemManager); UIApplication.SharedApplication.RegisterUserNotificationSettings(settings); UIApplication.SharedApplication.RegisterForRemoteNotifications(); LoadApplication(new App()); return base.FinishedLaunching(app, options); } 6. In **AppDelegate**, override the **RegisteredForRemoteNotifications** event: public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken) { // Modify device token string _deviceToken = deviceToken.Description; _deviceToken = _deviceToken.Trim('<', '>').Replace(" ", ""); // Get Mobile Services client MobileServiceClient client = todoItemManager.GetClient(); // Register for push with Mobile Services IEnumerable tag = new List() { "uniqueTag" }; const string template = "{\"aps\":{\"alert\":\"$(message)\"}}"; var expiryDate = DateTime.Now.AddDays(90).ToString (System.Globalization.CultureInfo.CreateSpecificCulture("en-US")); var push = client.GetPush(); push.RegisterTemplateAsync(_deviceToken, template, expiryDate, "myTemplate", tag); } 7. In **AppDelegate**, override the **ReceivedRemoteNotification** event: public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) { NSObject inAppMessage; bool success = userInfo.TryGetValue(new NSString("inAppMessage"), out inAppMessage); if (success) { var alert = new UIAlertView("Got push notification", inAppMessage.ToString(), null, "OK", null); alert.Show(); } } Your app is now updated to support push notifications. ### Update the registered insert script in the Azure classic portal 1. In the Azure classic portal, click the **Data** tab and then click the **TodoItem** table. ![][21] 2. In **todoitem**, click the **Script** tab and select **Insert**. ![][22] This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Execute the request and send notifications. request.execute({ success: function() { // Create a template-based payload. var payload = '{ "message" : "New item added: ' + item.text + '" }'; // Write the default response and send a notification // to all platforms. push.send(null, payload, { success: function(pushResponse){ console.log("Sent push:", pushResponse); // Send the default response. request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); // Send the an error response. request.respond(500, { error: pushResponse }); } }); } }); } This registers a new insert script, which sends a push notification (the inserted text) to the device provided in the insert request. >[AZURE.NOTE] This script delays sending the notification to give you time to close the app to receive a toast notification. ### Test push notifications in your app 1. Press the **Run** button to build the project and start the app in an iOS capable device, then click **OK** to accept push notifications >[AZURE.NOTE] You must explicitly accept push notifications from your app. This request only occurs the first time that the app runs. 2. In the app, click the **Add** button, add a task title and then click the **Save** button. 3. Verify that a notification is received, then click **OK** to dismiss the notification. You have successfully completed this tutorial. ## Add push notifications to the Xamarin.Forms.Android app You'll add push notifications to the Android app by using the Google Cloud Messaging (GCM) service. You'll need an active Google account, and the [Google Cloud Messaging Client Component]. ### Enable Google Cloud Messaging 1. Navigate to the [Google Cloud Console](https://console.developers.google.com/project), sign in with your Google account credentials. 2. Click **Create Project**, type a project name, then click **Create**. If requested, carry out the SMS Verification, and click **Create** again. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-new-project.png) Type in your new **Project name** and click **Create project**. 3. Click the **Utilities and More** button and then click **Project Information**. Make a note of the **Project Number**. You will need to set this value as the `SenderId` variable in the client app. ![](./media/mobile-services-enable-google-cloud-messaging/notification-hubs-utilities-and-more.png) 4. In the project dashboard, under **Mobile APIs**, click **Google Cloud Messaging**, then on the next page click **Enable API** and accept the terms of service. ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-GCM.png) ![Enabling GCM](./media/mobile-services-enable-google-cloud-messaging/enable-gcm-2.png) 5. In the project dashboard, Click **Credentials** > **Create Credential** > **API Key**. ![](./media/mobile-services-enable-google-cloud-messaging/mobile-services-google-create-server-key.png) 6. In **Create a new key**, click **Server key**, type a name for your key, then click **Create**. 7. Make a note of the **API KEY** value. You will use this API key value to enable Azure to authenticate with GCM and send push notifications on behalf of your app. ### Configure your mobile service to send push requests 1. Log on to the [Azure classic portal](https://manage.windowsazure.com/), click **Mobile Services**, and then click your app. 2. Click the **Push** tab, enter the **API Key** value obtained from GCM in the previous procedure, then click **Save**. ![](./media/mobile-services-android-configure-push/mobile-push-tab-android.png) >[AZURE.NOTE]When you set your GCM credentials for enhanced push notifications in the Push tab in the portal, they are shared with Notification Hubs to configure the notification hub with your app. Both your mobile service and your app are now configured to work with GCM and Notification Hubs. ### Update the registered insert script to send notifications >[AZURE.NOTE] The following steps show you how to update the script registered to the insert operation on the TodoItem table in the Azure classic portal. You can also access and edit this mobile service script directly in Visual Studio, in the Azure node of Server Explorer. In the [Azure classic portal], click the **Data** tab and then click the **TodoItem** table. ![][21] 2. In **todoitem**, click the **Script** tab and select **Insert**. ![][22] This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Execute the request and send notifications. request.execute({ success: function() { // Create a template-based payload. var payload = '{ "message" : "New item added: ' + item.text + '" }'; // Write the default response and send a notification // to all platforms. push.send(null, payload, { success: function(pushResponse){ console.log("Sent push:", pushResponse); // Send the default response. request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); // Send the an error response. request.respond(500, { error: pushResponse }); } }); } }); } This registers a new insert script, which sends a push notification (the inserted text) to the device provided in the insert request. >[AZURE.NOTE] This script delays sending the notification to give you time to close the app to receive a toast notification. ### Configure the existing project for push notifications 1. In the Solution view, expand the **Components** folder in the Xamarin.Android app and make sure that Azure Mobile Services package is installed. 2. Right-click the **Components** folder, click **Get More Components...**, search for the **Google Cloud Messaging Client** component and add it to the project. 1. Open the MainActivity.cs project file and add the following using statement to the class: using Gcm.Client; 4. In the **MainActivity** class, add the following code to the **OnCreate** method, after the call to the **LoadApplication** method: try { // Check to ensure everything's setup right GcmClient.CheckDevice(this); GcmClient.CheckManifest(this); // Register for push notifications System.Diagnostics.Debug.WriteLine("Registering..."); GcmClient.Register(this, PushHandlerBroadcastReceiver.SENDER_IDS); } catch (Java.Net.MalformedURLException) { CreateAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error"); } catch (Exception e) { CreateAndShowDialog(e, "Error"); } Your **MainActivity** is now prepared for adding push notifications. ### Add push notifications code to your app 5. In the ToDoAzure.Droid project, create a new class in the project called `GcmService`. 5. Add the following using statements to **GcmService** class: using Gcm.Client; using Microsoft.WindowsAzure.MobileServices; 6. Add the following permission requests between the **using** statements and the **namespace** declaration: [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")] [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")] //GET_ACCOUNTS is only needed for android versions 4.0.3 and below [assembly: UsesPermission(Name = "android.permission.GET_ACCOUNTS")] [assembly: UsesPermission(Name = "android.permission.INTERNET")] [assembly: UsesPermission(Name = "android.permission.WAKE_LOCK")] 7. In the **GcmService.cs** project file, add the following class: [BroadcastReceiver(Permission = Gcm.Client.Constants.PERMISSION_GCM_INTENTS)] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })] [IntentFilter(new string[] { Gcm.Client.Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })] public class PushHandlerBroadcastReceiver : GcmBroadcastReceiverBase { public static string[] SENDER_IDS = new string[] { "" }; } In the above code, you must replace _``_ with the project number assigned by Google when you provisioned your app in the Google developer portal. 8. In the GcmService.cs project file, add the following code that defines the **GcmService** class: [Service] public class GcmService : GcmServiceBase { public static string RegistrationID { get; private set; } public GcmService() : base(PushHandlerBroadcastReceiver.SENDER_IDS){} } Note that this class derives from **GcmServiceBase** and that the **Service** attribute must be applied to this class. >[AZURE.NOTE]The **GcmServiceBase** class implements the **OnRegistered()**, **OnUnRegistered()**, **OnMessage()** and **OnError()** methods. You must override these methods in the **GcmService** class. 5. Add the following code to the **GcmService** class that overrides the **OnRegistered** event handler. protected override void OnRegistered(Context context, string registrationId) { Log.Verbose(PushHandlerBroadcastReceiver.TAG, "GCM Registered: " + registrationId); RegistrationID = registrationId; createNotification("GcmService Registered...", "The device has been Registered, Tap to View!"); MobileServiceClient client = MainActivity.DefaultService.todoItemManager.GetClient; var push = client.GetPush(); MainActivity.DefaultService.RunOnUiThread(() => Register(push, null)); } public async void Register(Microsoft.WindowsAzure.MobileServices.Push push, IEnumerable tags) { try { const string template = "{\"data\":{\"message\":\"$(message)\"}}"; await push.RegisterTemplateAsync(RegistrationID, template, "mytemplate", tags); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); Debugger.Break(); } } This method uses the returned GCM registration ID to register with Azure for push notifications. 10. Override the **OnMessage** method in **GcmService** with the following code: protected override void OnMessage(Context context, Intent intent) { Log.Info(PushHandlerBroadcastReceiver.TAG, "GCM Message Received!"); var msg = new StringBuilder(); if (intent != null && intent.Extras != null) { foreach (var key in intent.Extras.KeySet()) msg.AppendLine(key + "=" + intent.Extras.Get(key).ToString()); } //Store the message var prefs = GetSharedPreferences(context.PackageName, FileCreationMode.Private); var edit = prefs.Edit(); edit.PutString("last_msg", msg.ToString()); edit.Commit(); string message = intent.Extras.GetString("message"); if (!string.IsNullOrEmpty(message)) { createNotification("New todo item!", "Todo item: " + message); return; } string msg2 = intent.Extras.GetString("msg"); if (!string.IsNullOrEmpty(msg2)) { createNotification("New hub message!", msg2); return; } createNotification("Unknown message details", msg.ToString()); } void createNotification(string title, string desc) { //Create notification var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager; //Create an intent to show ui var uiIntent = new Intent(this, typeof(MainActivity)); //Create the notification var notification = new Notification(Android.Resource.Drawable.SymActionEmail, title); //Auto cancel will remove the notification once the user touches it notification.Flags = NotificationFlags.AutoCancel; //Set the notification info //we use the pending intent, passing our ui intent over which will get called //when the notification is tapped. notification.SetLatestEventInfo(this, title, desc, PendingIntent.GetActivity(this, 0, uiIntent, 0)); //Show the notification notificationManager.Notify(1, notification); } 12. Add the following method overrides for **OnUnRegistered()** and **OnError()**, which are required for the project to compile. protected override void OnUnRegistered(Context context, string registrationId) { Log.Error("GcmService", "Unregistered RegisterationId : " + registrationId); } protected override void OnError(Context context, string errorId) { Log.Error(PushHandlerBroadcastReceiver.TAG, "GCM Error: " + errorId); } ### Test push notifications in your app You can test the app by directly attaching an Android phone with a USB cable, or by using a virtual device in the emulator. When you run this app in the emulator, make sure that you use an Android Virtual Device (AVD) that supports Google APIs. > [AZURE.IMPORTANT] In order to receive push notifications, you must set up a Google account on your Android Virtual Device (in the emulator, navigate to **Settings** and click **Add Account**). Also, make sure that the emulator is connected to the Internet. 1. From **Tools**, click **Open Android Emulator Manager**, select your device, and then click **Edit**. ![][125] 2. Select **Google APIs** in **Target**, then click **OK**. ![][126] 3. On the top toolbar, click **Run**, and then select your app. This starts the emulator and runs the app. The app retrieves the *registrationId* from GCM and registers with the Notification Hub. 1. In the app, add a new task. 2. Swipe down from the top of the screen to open the device's Notification Center to see the notification. ![][127] ## Add push notifications to the Xamarin.Forms.Windows app This section shows you how to use Azure Mobile Services to send push notifications to the Windows Phone Silverlight app that is included in your Xamarin.Forms solution. ### Update the app to register for notifications Before your app can receive push notifications, you must register a notification channel. 1. In Visual Studio, open the file App.xaml.cs and add the following `using` statement: using Microsoft.Phone.Notification; 3. Add the following to App.xaml.cs: public static HttpNotificationChannel CurrentChannel { get; private set; } private void AcquirePushChannel() { CurrentChannel = HttpNotificationChannel.Find("MyPushChannel"); if (CurrentChannel == null) { CurrentChannel = new HttpNotificationChannel("MyPushChannel"); CurrentChannel.Open(); CurrentChannel.BindToShellToast(); } CurrentChannel.ChannelUriUpdated += new EventHandler(async (o, args) => { // Register for notifications using the new channel const string template = "$(message)"; await client.GetPush() .RegisterTemplateAsync(CurrentChannel.ChannelUri.ToString(), template, "mytemplate"); }); } This code retrieves the ChannelURI for the app from the Microsoft Push Notification Service (MPNS) used by Windows Phone 8.x "Silverlight", and then registers that ChannelURI for push notifications. >[AZURE.NOTE]In this this tutorial, the mobile service sends a toast notification to the device. When you send a tile notification, you must instead call the **BindToShellTile** method on the channel. 4. At the top of the **Application_Launching** event handler in App.xaml.cs, add the following call to the new **AcquirePushChannel** method: AcquirePushChannel(); This makes sure that registration is requested every time that the page is loaded. In your app, you may only want to make this registration periodically to ensure that the registration is current. 5. Press the **F5** key to run the app. A popup dialog with the registration key is displayed. 6. In the Solution Explorer, expand **Properties**, open the WMAppManifest.xml file, click the **Capabilities** tab and make sure that the **ID_CAP_PUSH_NOTIFICATION** capability is checked. ![Enable notifications in VS](./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-app-enable-push-wp8.png) This makes sure that your app can raise toast notifications. ### Update server scripts to send push notifications Finally, you must update the script registered to the insert operation on the TodoItem table to send notifications. 1. In the [Azure classic portal], click the **Data** tab and then click the **TodoItem** table. ![][21] 2. In **todoitem**, click the **Script** tab and select **Insert**. ![][22] This displays the function that is invoked when an insert occurs in the **TodoItem** table. 3. Replace the insert function with the following code, and then click **Save**: function insert(item, user, request) { // Execute the request and send notifications. request.execute({ success: function() { // Create a template-based payload. var payload = '{ "message" : "New item added: ' + item.text + '" }'; // Write the default response and send a notification // to all platforms. push.send(null, payload, { success: function(pushResponse){ console.log("Sent push:", pushResponse); // Send the default response. request.respond(); }, error: function (pushResponse) { console.log("Error Sending push:", pushResponse); // Send the an error response. request.respond(500, { error: pushResponse }); } }); } }); } This registers a new insert script, which sends a push notification (the inserted text) to the device provided in the insert request. 3. Click the **Push** tab, check **Enable unauthenticated push notifications**, then click **Save**. This enables the mobile service to connect to MPNS in unauthenticated mode to send push notifications. >[AZURE.NOTE]This tutorial uses MPNS in unauthenticated mode. In this mode, MPNS limits the number of notifications that can be sent to a device channel. To remove this restriction, you must generate and upload a certificate by clicking **Upload** and selecting the certificate. For more information on generating the certificate, see [Setting up an authenticated web service to send push notifications for Windows Phone]. ### Test push notifications in your app 1. In Visual Studio, press the F5 key to run the app. >[AZURE.NOTE] You may encounter a 401 Unauthorized RegistrationAuthorizationException when testing on the Windows Phone emulator. This can occur during the `RegisterNativeAsync()` call because of the way the Windows Phone emulator syncs it's clock with the host PC. It can result in a security token that will be rejected. To resolve this simply manually set the clock in the emulator before testing. 5. In the app, create a new task with the title **Hello push**, then immediately click the start button or back button to leave the app. This sends an insert request to the mobile service to store the added item. Notice that the device receives a toast notification that says **hello push**. ![Toast notification received](./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-quickstart-push5-wp8.png) >[AZURE.NOTE]You will not receive the notification when you are still in the app. To receive a toast notification while the app is active, you must handle the [ShellToastNotificationReceived](http://msdn.microsoft.com/library/windowsphone/develop/microsoft.phone.notification.httpnotificationchannel.shelltoastnotificationreceived(v=vs.105).aspx) event. [Generate the certificate signing request]: #certificates [Register your app and enable push notifications]: #register [Create a provisioning profile for the app]: #profile [Configure Mobile Services]: #configure-mobileServices [Configure the Xamarin.iOS App]: #configure-app [Update scripts to send push notifications]: #update-scripts [Add push notifications to the app]: #add-push [Insert data to receive notifications]: #test [5]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step5.png [6]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step6.png [7]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step7.png [9]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step9.png [10]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step10.png [17]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step17.png [18]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-selection.png [19]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-push-tab-ios.png [20]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-push-tab-ios-upload.png [21]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-portal-data-tables.png [22]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-insert-script-push2.png [23]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-quickstart-push1-ios.png [24]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-quickstart-push2-ios.png [25]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-quickstart-push3-ios.png [26]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-quickstart-push4-ios.png [28]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-step18.png [101]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-01.png [102]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-02.png [103]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-03.png [104]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-04.png [105]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-05.png [106]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-06.png [107]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-07.png [108]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-08.png [110]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-10.png [111]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-11.png [112]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-12.png [113]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-13.png [114]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-14.png [115]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-15.png [116]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-16.png [117]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-17.png [120]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-20.png [121]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-21.png [122]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-services-ios-push-22.png [123]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-data-tab-empty.png [124]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/mobile-create-todoitem-table.png [125]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/notification-hub-create-android-app7.png [126]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/notification-hub-create-android-app8.png [127]: ./media/partner-xamarin-mobile-services-xamarin-forms-get-started-push/notification-area-received.png [Xamarin Studio]: http://xamarin.com/download [Install Xcode]: https://go.microsoft.com/fwLink/p/?LinkID=266532 [iOS Provisioning Portal]: http://go.microsoft.com/fwlink/p/?LinkId=272456 [Mobile Services iOS SDK]: https://go.microsoft.com/fwLink/p/?LinkID=266533 [Apple Push Notification Service]: http://go.microsoft.com/fwlink/p/?LinkId=272584 [Get started with Mobile Services]: mobile-services-ios-get-started.md [Xamarin Device Provisioning]: http://developer.xamarin.com/guides/ios/getting_started/installation/device_provisioning/ [Azure classic portal]: https://manage.windowsazure.com/ [apns object]: http://go.microsoft.com/fwlink/p/?LinkId=272333 [Azure Mobile Services Component]: http://components.xamarin.com/view/azure-mobile-services/ [completed example project]: http://go.microsoft.com/fwlink/p/?LinkId=331303 [Google Cloud Messaging Client Component]: http://components.xamarin.com/view/GCMClient/ [Xamarin.Forms Azure Push Notification Starter Sample]: https://github.com/Azure/mobile-services-samples/tree/master/TodoListXamarinForms [Completed Xamarin.Forms Azure Push Notification Sample]: https://github.com/Azure/mobile-services-samples/tree/master/GettingStartedWithPushXamarinForms ================================================ FILE: docs/store-sendgrid-mobile-services-send-email-scripts.md ================================================ # Send email from Mobile Services with SendGrid >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/).   This topic shows you how can add email functionality to your mobile service. In this tutorial you add server side scripts to send email using SendGrid. When complete, your mobile service will send an email each time a record is inserted. SendGrid is a [cloud-based email service] that provides reliable [transactional email delivery], scalability, and real-time analytics along with flexible APIs that make custom integration easy. For more information, see . This tutorial walks you through these basic steps to enable email functionality: 1. [Create a SendGrid account] 2. [Add a script to send email] 3. [Insert data to receive email] This tutorial is based on the Mobile Services quickstart. Before you start this tutorial, you must first complete [Get started with Mobile Services]. ## Create a new SendGrid account Azure customers can unlock 25,000 free emails each month. These 25,000 free monthly emails will give you access to advanced reporting and analytics and [all APIs][] (Web, SMTP, Event, Parse and more). For information about additional services provided by SendGrid, see the [SendGrid Features][] page. ### To sign up for a SendGrid account 1. Log in to the [Azure Management Portal][]. 2. In the lower pane of the management portal, click **New**. ![command-bar-new][command-bar-new] 3. Click **Marketplace**. ![sendgrid-store][sendgrid-store] 4. In the **Choose an Application and Service** dialog, select **SendGrid** and click the right arrow. 5. In the **Personalize Application and Service** dialog select the **SendGrid** plan you want to sign up for. 6. Enter a name to identify your **SendGrid** service in your Azure settings, or use the default value of **SendGridEmailDelivery.Simplified.SMTPWebAPI**. Names must be between 1 and 100 characters in length and contain only alphanumeric characters, dashes, dots, and underscores. The name must be unique in your list of subscribed Azure Store Items. ![store-screen-2][store-screen-2] 7. Choose a value for the region; for example, West US. 8. Click the right arrow. 9. On the **Review Purchase** tab, review the plan and pricing information, and review the legal terms. If you agree to the terms, click the check mark. After you click the check mark, your SendGrid account will begin the [SendGrid provisioning process]. ![store-screen-3][store-screen-3] 10. After confirming your purchase you are redirected to the add-ons dashboard and you will see the message **Purchasing SendGrid**. ![sendgrid-purchasing-message][sendgrid-purchasing-message] Your SendGrid account is provisioned immediately and you will see the message **Successfully purchased Add-On SendGrid**. Your account and credentials are now created. You are ready to send emails at this point. To modify your subscription plan or see the SendGrid contact settings, click the name of your SendGrid service to open the SendGrid Marketplace dashboard. ![sendgrid-add-on-dashboard][sendgrid-add-on-dashboard] To send an email using SendGrid, you must supply your account credentials (username and password). ### To find your SendGrid credentials ### 1. Click **Connection Info**. ![sendgrid-connection-info-button][sendgrid-connection-info-button] 2. In the *Connection info* dialog, copy the **Password** and Username to use later in this tutorial. ![sendgrid-connection-info][sendgrid-connection-info] To set your email deliverability settings, click the **Manage** button. This will redirect to your SendGrid Control Panel. ![sendgrid-control-panel][sendgrid-control-panel] For more information on getting started with SendGrid, see [SendGrid Getting Started][]. ## Register a new script that sends emails 1. Log on to the [Azure classic portal], click **Mobile Services**, and then click your mobile service. 2. In the Azure classic portal, click the **Data** tab and then click the **TodoItem** table. ![][1] 3. In **todoitem**, click the **Script** tab and select **Insert**. ![][2] This displays the function that is invoked when an insert occurs in the **TodoItem** table. 4. Replace the insert function with the following code: var SendGrid = require('sendgrid').SendGrid; function insert(item, user, request) { request.execute({ success: function() { // After the record has been inserted, send the response immediately to the client request.respond(); // Send the email in the background sendEmail(item); } }); function sendEmail(item) { var sendgrid = new SendGrid('**username**', '**password**'); sendgrid.send({ to: '**email-address**', from: '**from-address**', subject: 'New to-do item', text: 'A new to-do was added: ' + item.text }, function(success, message) { // If the email failed to send, log it as an error so we can investigate if (!success) { console.error(message); } }); } } 5. Replace the placeholders in the above script with the correct values: - **_username_ and _password_**: the SendGrid credentials you identified in [Create a SendGrid account]. - **_email-address_**: the address that the email is sent to. In a real-world app, you can use tables to store and retrieve email addresses. When testing your app, just use your own email address. - **_from-address_**: the address from which the email originates. Consider using a registered domain address that belongs to your organization. > [AZURE.NOTE] If you do not have a registered domain, you can instead use the domain of your Mobile Service, in the format *notifications@_your-mobile-service_.azure-mobile.net*. However, messages sent to your mobile service domain are ignored. 6. Click the **Save** button. You have now configured a script to send an email each time a record is inserted into the **TodoItem** table. ## Insert test data to receive email 1. In the client app project, run the quickstart application. This topic shows the Windows Store version of the quickstart, 2. In the app, type text in **Insert a TodoItem**, and then click **Save**. ![][3] 3. Notice that you receive an email, such as one shown in the notification below. ![][4] Congratulations, you have successfully configured your mobile service to send email by using SendGrid. ## Next Steps Now that you've seen how easy it is to use the SendGrid email service with Mobile Services, follow these links to learn more about SendGrid. - SendGrid API documentation: - SendGrid special offer for Azure customers: [command-bar-new]: ./media/sendgrid-sign-up/sendgrid_BAR_NEW.PNG [sendgrid-store]: ./media/sendgrid-sign-up/sendgrid_offerings_store.png [store-screen-2]: ./media/sendgrid-sign-up/sendgrid_store_scrn2.png [store-screen-3]: ./media/sendgrid-sign-up/sendgrid_store_scrn3.png [sendgrid-purchasing-message]: ./media/sendgrid-sign-up/sendgrid_purchasing_message.png [sendgrid-add-on-dashboard]: ./media/sendgrid-sign-up/sendgrid_add-on_dashboard.png [sendgrid-connection-info]: ./media/sendgrid-sign-up/sendgrid_connection_info.png [sendgrid-connection-info-button]: ./media/sendgrid-sign-up/sendgrid_connection_info_button.png [sendgrid-control-panel]: ./media/sendgrid-sign-up/sendgrid_control_panel.png [SendGrid Features]: http://sendgrid.com/features [Azure Management Portal]: https://manage.windowsazure.com [SendGrid Getting Started]: http://sendgrid.com/docs [SendGrid Provisioning Process]: https://support.sendgrid.com/hc/articles/200181628-Why-is-my-account-being-provisioned- [all APIs]: https://sendgrid.com/docs/API_Reference/index.html [Create a SendGrid account]: #sign-up [Add a script to send email]: #add-script [Insert data to receive email]: #insert-data [1]: ./media/store-sendgrid-mobile-services-send-email-scripts/mobile-portal-data-tables.png [2]: ./media/store-sendgrid-mobile-services-send-email-scripts/mobile-insert-script-push2.png [3]: ./media/store-sendgrid-mobile-services-send-email-scripts/mobile-quickstart-push1.png [4]: ./media/store-sendgrid-mobile-services-send-email-scripts/mobile-receive-email.png [Get started with Mobile Services]: https://azure.microsoft.com/develop/mobile/tutorials/get-started [sign up page]: https://sendgrid.com/windowsazure.html [Multiple User Credentials page]: https://sendgrid.com/credentials [Azure classic portal]: https://manage.windowsazure.com/ [cloud-based email service]: https://sendgrid.com/email-solutions [transactional email delivery]: https://sendgrid.com/transactional-email ================================================ FILE: docs/vs-mobile-services-cordova-getting-started.md ================================================ # Getting Started with Mobile Services (Cordova Projects) >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## First steps The first step you need to do in order to follow the code in these examples depends on what type of mobile service you connected to. - For a JavaScript backend mobile service, create a table called TodoItem. To create a table, locate the mobile service under the Azure node in Server Explorer, right-click the mobile service's node to open the context menu, and choose **Create Table**. Enter "TodoItem" as the table name. - If you have a .NET backend mobile service, there's already a TodoItem table in the default project template that Visual Studio created for you, but you need to publish it to Azure. To publish it, open the context menu for the mobile service project in Solution Explorer, and choose **Publish Web**. Accept the defaults, and choose the **Publish** button. ## Create a reference to a table The following code gets a reference to a table that contains data for a TodoItem, which you can use in subsequent operations to read and update the data table. The TodoItem table is created automatically when you create a mobile service. var todoTable = mobileServiceClient.getTable('TodoItem'); For these examples to work, permissions on the table must be set to **Anybody with an Application Key**. Later, you can set up authentication. See [Get started with authentication](mobile-services-html-get-started-users.md). ## Add an item to a table Insert a new item into a data table. An id (a GUID of type string) is automatically created as the primary key for the new row. Call the **done** method on the returned [Promise](https://msdn.microsoft.com/library/dn802826.aspx) object to get a copy of the inserted object and handle any errors. function TodoItem(text) { this.text = text; this.complete = false; } var items = new Array(); var insertTodoItem = function (todoItem) { todoTable.insert(todoItem).done(function (item) { items.push(item) }); }; ## Read or query a table The following code queries a table for all items, sorted by the text field. You can add code to process the query results in the success handler. In this case, a local array of the items is updated. todoTable.orderBy('text') .read().done(function (results) { items = results.slice(); }); You can use the where method to modify the query. Here's an example that filters out completed items. todoTable.where(function () { return (this.complete === false); }) .read().done(function (results) { items = results.slice(); }); For more examples of queries you can use, see [query](https://msdn.microsoft.com/library/azure/jj613353.aspx) object. ## Update a table item Update a row in a data table. In this code, when the mobile service responds, the item is removed from the list. Call the **done** method on the returned [Promise](https://msdn.microsoft.com/library/dn802826.aspx) object to get a copy of the inserted object and handle any errors. todoTable.update(todoItem).done(function (item) { // Update a local collection of items. items.splice(items.indexOf(todoItem), 1, item); }); ## Delete a table item Delete a row in a data table using the **del** method. Call the **done** method on the returned [Promise](https://msdn.microsoft.com/library/dn802826.aspx) object to get a copy of the inserted object and handle any errors. todoTable.del(todoItem).done(function (item) { items.splice(items.indexOf(todoItem), 1); }); ================================================ FILE: docs/vs-mobile-services-cordova-what-happened.md ================================================ # What happened to my Azure Cordova project after adding Azure Mobile Services by using Visual Studio Connected Services? ## References Added The Azure Mobile Service Client plugin included with all Multi-Device Hybrid Apps has been enabled. ## Connection string values for Mobile Services Under `services\mobileServices\settings`, a new JavaScript (.js) file with a **MobileServiceClient** was generated containing the selected mobile service’s application URL and application key. The file contains the initialization of a mobile service client object, similar to the following code. var mobileServiceClient; document.addEventListener("deviceready", function() { mobileServiceClient = new WindowsAzure.MobileServiceClient( ".azure-mobile.net", "" ); [Learn more about mobile services](https://azure.microsoft.com/documentation/services/mobile-services/) ================================================ FILE: docs/vs-mobile-services-dotnet-getting-started.md ================================================ # Getting Started with Mobile Services (.NET Projects) >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). The first step you need to do in order to follow the code in these examples depends on what type of mobile service you connected to. - For a JavaScript backend mobile service, create a table called TodoItem. To create a table, locate the mobile service under the Azure node in Server Explorer, right-click the mobile service's node to open the context menu, and choose **Create Table**. Enter "TodoItem" as the table name. - If you have a .NET backend mobile service, there's already a TodoItem table in the default project template that Visual Studio created for you, but you need to publish it to Azure. To publish it, open the context menu for the mobile service project in Solution Explorer, and choose **Publish Web**. Accept the defaults, and choose the **Publish** button. ## Get a reference to a table The following code creates a reference to a table (`todoTable`) that contains data for a TodoItem, which you can use in subsequent operations to read and update the data table. You'll need the TodoItem class with attributes set up to interpet the JSON that the mobile service sends in response to your queries. public class TodoItem { public string Id { get; set; } [JsonProperty(PropertyName = "text")] public string Text { get; set; } [JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } } IMobileServiceTable todoTable = App..GetTable(); This code works if your table has permissions set to **Anybody with an Application Key**. If you change the permissions to secure your mobile service, you'll need to add user authentication support. See [Get Started with Authentication](mobile-services-dotnet-backend-windows-universal-dotnet-get-started-users.md). ## Add a table item Insert a new item into a data table. TodoItem todoItem = new TodoItem() { Text = "My first to do item", Complete = false }; await todoTable.InsertAsync(todoItem); ## Read or query a table The following code queries a table for all items. Note that it returns only the first page of data, which by default is 50 items. You can pass the page size you want, since it's an optional parameter. List items; try { // Query that returns all items. items = await todoTable.ToListAsync(); } catch (MobileServiceInvalidOperationException e) { // handle exception } ## Update a table item Update a row in a data table. The parameter item is the TodoItem object to be updated. await todoTable.UpdateAsync(item); ## Delete a table item Delete a row in the database. The parameter item is the TodoItem object to be deleted. await todoTable.DeleteAsync(item); [Learn more about mobile services](https://azure.microsoft.com/documentation/services/mobile-services/) ================================================ FILE: docs/vs-mobile-services-dotnet-what-happened.md ================================================ # What happened to my Visual Studio .NET project after adding Azure Mobile Services by using Connected Services? >[AZURE.WARNING] This is an **Azure Mobile Services** topic. This service has been superseded by Azure App Service Mobile Apps and is scheduled for removal from Azure. We recommend using Azure Mobile Apps for all new mobile backend deployments. Read [this announcement](https://azure.microsoft.com/blog/transition-of-azure-mobile-services/) to learn more about the pending deprecation of this service. > > Learn about [migrating your site to Azure App Service](https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-migrating-from-mobile-services/). > > Get started with Azure Mobile Apps, see the [Azure Mobile Apps documentation center](https://azure.microsoft.com/documentation/learning-paths/appservice-mobileapps/). ## References Added The Azure Mobile Services NuGet package was added to your project. As a result, the following .NET references were added to your project: - **Microsoft.WindowsAzure.Mobile** - **Microsoft.WindowsAzure.Mobile.Ext** - **Newtonsoft.Json** - **System.Net.Http.Extensions** - **System.Net.Http.Primitives** ## Connection string values for Mobile Services In your App.xaml.cs file, a **MobileServiceClient** object was created with the selected mobile service’s application URL and application key. ## Mobile Services project added If a .NET mobile service is created in the Connected Service Provider, then a mobile services project is created and added to the solution. [Learn more about mobile services](https://azure.microsoft.com/documentation/services/mobile-services/) ================================================ FILE: docs/vs-mobile-services-javascript-getting-started.md ================================================ # Getting Started with with a Javascript mobile app after adding Azure Mobile Services by using Visual Studio Connected Services The first step you need to do in order to follow the code in these examples depends on what type of mobile service you connected to. - For a JavaScript backend mobile service, create a table called TodoItem. To create a table, locate the mobile service under the Azure node in Server Explorer, right-click the mobile service's node to open the context menu, and choose **Create Table**. Enter "TodoItem" as the table name. - If instead you have a .NET backend mobile service, there's already a TodoItem table in the default project template that Visual Studio created for you, but you need to publish it to Azure. To publish it, open the context menu for the mobile service project in Solution Explorer, and choose **Publish Web**. Accept the defaults, and choose the **Publish** button. ## Get a reference to a table The client object was added to your project already. Its name is the name of your mobile service with "Client" appended to it. The following code gets a reference to a table that contains data for a TodoItem, which you can use in subsequent operations to read and update the data table. var todoTable = yourMobileServiceClient.getTable('TodoItem'); ## Add an entry Insert a new item into a data table. An id (a GUID of type string) is automatically created as the primary key for the new row. Don't change the type of the id column, since the mobile services infrastructure uses it. var todoTable = client.getTable('TodoItem'); var todoItems = new WinJS.Binding.List(); var insertTodoItem = function (todoItem) { todoTable.insert(todoItem).done(function (item) { todoItems.push(item); }); }; ## Read/query a table The following code queries a table for all items, updates a local collection and binds the result to the UI element listItems. // This code refreshes the entries in the list view // by querying the TodoItems table. todoTable.where() .read() .done(function (results) { todoItems = new WinJS.Binding.List(results); listItems.winControl.itemDataSource = todoItems.dataSource; }); You can use the where method to modify the query. Here's an example that filters out completed items. todoTable.where(function () { return (this.complete === false && this.createdAt !== null); }) .read() .done(function (results) { todoItems = new WinJS.Binding.List(results); listItems.winControl.itemDataSource = todoItems.dataSource; }); For more examples of queries you can use, see [query object](http://msdn.microsoft.com/library/azure/jj613353.aspx). ## Update an entry Update a row in a data table. In this example, *todoItem* is the updated item, and *item* is the same item as returned from the mobile service. When the mobile service responds, the item is updated in the local todoItems list using the [splice](http://msdn.microsoft.com/library/windows/apps/Hh700810.aspx) method. Call the **done** method on the returned [Promise](https://msdn.microsoft.com/library/dn802826.aspx) object to get a copy of the inserted object and handle any errors. todoTable.update(todoItem).done(function (item) { todoItems.splice(todoItems.indexOf(item), 1, item); }); ## Delete an entry Delete a row in a data table. Call the [done]() method on the returned [Promise](https://msdn.microsoft.com/library/dn802826.aspx) object to get a copy of the inserted object and handle any errors. todoTable.del(todoItem).done(function (item) { todoItems.splice(todoItems.indexOf(item), 1); } [Learn more about mobile services](https://azure.microsoft.com/documentation/services/mobile-services/) ================================================ FILE: docs/vs-mobile-services-javascript-what-happened.md ================================================ # What happens to my Javascript project when I add Azure Mobile Services using Connected Visual Studio Services? ## NuGet package added The **WindowsAzure.MobileServices.WinJS** NuGet package was installed, including the Azure Mobile Service library in the `js\MobileServices.js` file. ## Connection string values for Mobile Services In the `services\mobileServices\settings` folder, a new JavaScript (.js) file with a **MobileServiceClient** was generated that contains the selected mobile service's application URL and application key. ## References added to default.html References to `MobileServices.js` and the settings file were added to `default.html`. ## Connected services files added In the services folder, Connected Services configuration files were added. ================================================ FILE: sdk/Javascript/.gitignore ================================================ *.suo #ignore build files bin obj bld *.jsproj.user test/*/js/MobileServices.* packages/* #just in case ignore the folders we pull from git (if a delete fails in the build process) tools/JSBuild/queryjs/* tools/JSBuild/esprima/* node_modules ================================================ FILE: sdk/Javascript/Gruntfile.js ================================================ /// module.exports = function (grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), files: { resources: [ 'src/Strings/**/Resources.resjson' ], core: [ 'src/Utilities/Extensions.js', 'src/MobileServiceClient.js', 'src/MobileServiceTable.js', 'src/MobileServiceLogin.js', 'src/Push/RegistrationManager.js', 'src/Push/LocalStorageManager.js', 'src/Push/PushHttpClient.js', 'src/Utilities/Validate.js', 'src/External/queryjs/lib/*.js', 'src/External/esprima/esprima.js' ], web: [ 'src/Push/Push.Web.js', 'src/Platforms/Platform.Web.js', 'src/Generated/MobileServices.Core.js', 'src/Transports/*.js', 'src/LoginUis/*.js', 'src/Utilities/PostMessageExchange.js', 'src/Utilities/Promises.js' ], winjs: [ 'src/Push/Push.WinJS.js', 'src/LoginUis/WebAuthBroker.js', 'src/Platforms/Platform.WinJS.js', ], node: [ 'src/Internals/NodeExports.js', ], Internals: [ 'src/Internals/InternalsVisible.js', ], Intellisense: [ 'src/Internals/DevIntellisense.js', ], }, jshint: { all: ['Gruntfile.js', 'src/**/*.js', '!src/External/**/*.js', '!src/Generated/*.js', 'test/**/*.js', '!test/**/bin/**', '!**/MobileServices.*.js'] }, concat: { options: { stripBanners: true, banner: header, process: wrapModule, footer: footer }, resources: { options: { banner: '\n\t$__modules__.Resources = { };\n\n', process: wrapResourceFile, footer: '' }, src: ['<%= files.resources %>'], dest: 'src/Generated/Resources.js' }, web: { src: ['src/Require.js', 'src/Generated/Resources.js', '<%= files.core %>', '<%= files.web %>'], dest: 'src/Generated/MobileServices.Web.js' }, webinternals: { options: { footer: '\n\trequire(\'InternalsVisible\');' + footer }, src: ['src/Require.js', 'src/Generated/Resources.js', '<%= files.Internals %>', '<%= files.core %>', '<%= files.web %>'], dest: 'src/Generated/MobileServices.Web.Internals.js' }, winjs: { src: ['src/Require.js', 'src/Generated/Resources.js', '<%= files.core %>', '<%= files.winjs %>'], dest: 'src/Generated/MobileServices.js' }, winjsinternals: { options: { footer: '\n\trequire(\'InternalsVisible\');' + footer }, src: ['src/Require.js', 'src/Generated/Resources.js', '<%= files.Internals %>', '<%= files.core %>', '<%= files.winjs %>'], dest: 'src/Generated/MobileServices.Internals.js' }, Intellisense: { options: { footer: '\n\trequire(\'DevIntellisense\');' + footer }, src: ['src/Require.js', '<%= files.core %>', '<%= files.winjs %>', '<%= files.Intellisense %>'], dest: 'src/Generated/MobileServices.DevIntellisense.js' } }, uglify: { options: { banner: '//! Copyright (c) Microsoft Corporation. All rights reserved. <%= pkg.name %> v<%= pkg.version %>\n', mangle: false }, web: { src: 'src/Generated/MobileServices.Web.js', dest: 'src/Generated/MobileServices.Web.min.js' }, winjs: { src: 'src/Generated/MobileServices.js', dest: 'src/Generated/MobileServices.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-concat'); // Default task(s). grunt.registerTask('default', ['jshint', 'concat', 'uglify']); }; var header = '// ----------------------------------------------------------------------------\n' + '// Copyright (c) Microsoft Corporation. All rights reserved\n' + '// <%= pkg.name %> - v<%= pkg.version %>\n' + '// ----------------------------------------------------------------------------\n' + '\n' + '(function (global) {\n' + '\tvar $__fileVersion__ = \'<%= pkg.version %>\';\n', footer = '\n\trequire(\'MobileServiceClient\');\n' + '})(this || exports);'; function wrapModule(src, filepath) { /// /// Takes a file, and if it should be a module, wraps the code in a module block /// /// /// Source code of a module file /// /// /// Sile path of the module (i.e. src/MobileServicesClient.js) /// var lastSlash = filepath.lastIndexOf('/'), name = filepath.substr(lastSlash + 1); name = name.substring(0, name.indexOf('.')); if (name == 'Require' || name == 'Resources') { return src; } var newSrc = src.replace(/\/\/\/\s<[\w\s=":\\().]+\/>\n/g, ''); newSrc = '\t\t' + newSrc.replace(/\n/g, '\n\t\t'); return '\n\t$__modules__.' + name + ' = function (exports) {\n' + newSrc + '\n\t};'; } function wrapResourceFile(src, filepath) { /// /// Takes a resjson file and places it into a module level resources array /// with the index corresponding to the language identifier in the file path /// /// /// Source code of a module file /// /// /// File path of the resjson (i.e. src/Strings/en-US/Resources.resjson) /// The file name must be in format of //Resources.resjson /// var language = filepath.replace('src/Strings/', '').replace('/Resources.resjson', ''), newSrc = src.replace(/\n/g, '\n\t\t'); return '\t$__modules__.Resources[\'' + language + '\'] = ' + newSrc + ';'; } ================================================ FILE: sdk/Javascript/Microsoft.WindowsAzure.Mobile.JS.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0.30723.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WindowsAzure.Mobile.JS", "src\Microsoft.WindowsAzure.Mobile.JS.csproj", "{0D39CF6F-4884-4D43-8744-2CEB723629C8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2BDD39FE-2E9F-4B97-8173-02037CF765AA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F52A1420-8C01-42ED-8EF2-71F83EE49A87}" EndProject Project("{262852C6-CD72-467D-83FE-5EEB1973A190}") = "Microsoft.WindowsAzure.Mobile.WinJS.Test", "test\winJS\Microsoft.WindowsAzure.Mobile.WinJS.Test.jsproj", "{96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}" ProjectSection(ProjectDependencies) = postProject {0D39CF6F-4884-4D43-8744-2CEB723629C8} = {0D39CF6F-4884-4D43-8744-2CEB723629C8} EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WindowsAzure.Mobile.WinJS.TestFramework", "test\framework\Microsoft.WindowsAzure.Mobile.WinJS.TestFramework.csproj", "{6445515D-9E58-4812-BAD6-BC81DAF1C0EE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{8F22488F-603B-4BA2-9AA0-DBC7D4487978}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.WindowsAzure.Mobile.JSBuild", "tools\JSBuild\Microsoft.WindowsAzure.Mobile.JSBuild.csproj", "{FFD91CC6-07FF-4D69-A064-2A911E2B4087}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Zumo.Web.Test", "test\web\Microsoft.Azure.Zumo.Web.Test.csproj", "{5AA504E2-10A2-4179-9645-47E5CF857DDF}" ProjectSection(ProjectDependencies) = postProject {0D39CF6F-4884-4D43-8744-2CEB723629C8} = {0D39CF6F-4884-4D43-8744-2CEB723629C8} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B7E1D498-5891-43F0-80A6-6DE18515433C}" ProjectSection(SolutionItems) = preProject gruntfile.js = gruntfile.js package.json = package.json EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution BuildTools|Any CPU = BuildTools|Any CPU BuildTools|ARM = BuildTools|ARM BuildTools|x64 = BuildTools|x64 BuildTools|x86 = BuildTools|x86 Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM Release|x64 = Release|x64 Release|x86 = Release|x86 Signed|Any CPU = Signed|Any CPU Signed|ARM = Signed|ARM Signed|x64 = Signed|x64 Signed|x86 = Signed|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|Any CPU.ActiveCfg = BuildTools|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|ARM.ActiveCfg = BuildTools|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|ARM.Build.0 = BuildTools|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|x64.ActiveCfg = BuildTools|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|x64.Build.0 = BuildTools|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|x86.ActiveCfg = BuildTools|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.BuildTools|x86.Build.0 = BuildTools|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|ARM.ActiveCfg = Debug|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|ARM.Build.0 = Debug|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|x64.ActiveCfg = Debug|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|x64.Build.0 = Debug|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|x86.ActiveCfg = Debug|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Debug|x86.Build.0 = Debug|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|Any CPU.Build.0 = Release|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|ARM.ActiveCfg = Release|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|ARM.Build.0 = Release|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|x64.ActiveCfg = Release|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|x64.Build.0 = Release|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|x86.ActiveCfg = Release|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Release|x86.Build.0 = Release|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|Any CPU.ActiveCfg = Release|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|Any CPU.Build.0 = Release|Any CPU {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|ARM.ActiveCfg = Release|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|ARM.Build.0 = Release|ARM {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|x64.ActiveCfg = Release|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|x64.Build.0 = Release|x64 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|x86.ActiveCfg = Release|x86 {0D39CF6F-4884-4D43-8744-2CEB723629C8}.Signed|x86.Build.0 = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|Any CPU.ActiveCfg = BuildTools|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|ARM.ActiveCfg = BuildTools|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|ARM.Build.0 = BuildTools|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|ARM.Deploy.0 = BuildTools|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x64.ActiveCfg = BuildTools|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x64.Build.0 = BuildTools|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x64.Deploy.0 = BuildTools|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x86.ActiveCfg = BuildTools|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x86.Build.0 = BuildTools|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.BuildTools|x86.Deploy.0 = BuildTools|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|Any CPU.Build.0 = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|ARM.ActiveCfg = Debug|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|ARM.Build.0 = Debug|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|ARM.Deploy.0 = Debug|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x64.ActiveCfg = Debug|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x64.Build.0 = Debug|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x64.Deploy.0 = Debug|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x86.ActiveCfg = Debug|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x86.Build.0 = Debug|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Debug|x86.Deploy.0 = Debug|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|Any CPU.ActiveCfg = Release|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|Any CPU.Build.0 = Release|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|Any CPU.Deploy.0 = Release|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|ARM.ActiveCfg = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|ARM.Build.0 = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|ARM.Deploy.0 = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x64.ActiveCfg = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x64.Build.0 = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x64.Deploy.0 = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x86.ActiveCfg = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x86.Build.0 = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Release|x86.Deploy.0 = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|Any CPU.ActiveCfg = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|Any CPU.Build.0 = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|Any CPU.Deploy.0 = Debug|Any CPU {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|ARM.ActiveCfg = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|ARM.Build.0 = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|ARM.Deploy.0 = Release|ARM {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x64.ActiveCfg = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x64.Build.0 = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x64.Deploy.0 = Release|x64 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x86.ActiveCfg = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x86.Build.0 = Release|x86 {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C}.Signed|x86.Deploy.0 = Release|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|Any CPU.ActiveCfg = BuildTools|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|ARM.ActiveCfg = BuildTools|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|ARM.Build.0 = BuildTools|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|x64.ActiveCfg = BuildTools|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|x64.Build.0 = BuildTools|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|x86.ActiveCfg = BuildTools|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.BuildTools|x86.Build.0 = BuildTools|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|ARM.ActiveCfg = Debug|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|ARM.Build.0 = Debug|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|x64.ActiveCfg = Debug|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|x64.Build.0 = Debug|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|x86.ActiveCfg = Debug|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Debug|x86.Build.0 = Debug|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|Any CPU.Build.0 = Release|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|ARM.ActiveCfg = Release|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|ARM.Build.0 = Release|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|x64.ActiveCfg = Release|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|x64.Build.0 = Release|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|x86.ActiveCfg = Release|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Release|x86.Build.0 = Release|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|Any CPU.ActiveCfg = Signed|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|Any CPU.Build.0 = Signed|Any CPU {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|ARM.ActiveCfg = Signed|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|ARM.Build.0 = Signed|ARM {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|x64.ActiveCfg = Signed|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|x64.Build.0 = Signed|x64 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|x86.ActiveCfg = Signed|x86 {6445515D-9E58-4812-BAD6-BC81DAF1C0EE}.Signed|x86.Build.0 = Signed|x86 {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.BuildTools|Any CPU.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.BuildTools|Any CPU.Build.0 = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.BuildTools|ARM.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.BuildTools|x64.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.BuildTools|x86.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Debug|Any CPU.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Debug|ARM.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Debug|x64.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Debug|x86.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Release|Any CPU.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Release|Any CPU.Build.0 = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Release|ARM.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Release|x64.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Release|x86.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Signed|Any CPU.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Signed|Any CPU.Build.0 = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Signed|ARM.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Signed|x64.ActiveCfg = BuildTools|Any CPU {FFD91CC6-07FF-4D69-A064-2A911E2B4087}.Signed|x86.ActiveCfg = BuildTools|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.BuildTools|Any CPU.ActiveCfg = BuildTools|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.BuildTools|ARM.ActiveCfg = BuildTools|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.BuildTools|x64.ActiveCfg = BuildTools|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.BuildTools|x86.ActiveCfg = BuildTools|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Debug|ARM.ActiveCfg = Debug|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Debug|x64.ActiveCfg = Debug|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Debug|x86.ActiveCfg = Debug|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Release|Any CPU.Build.0 = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Release|ARM.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Release|x64.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Release|x86.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Signed|Any CPU.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Signed|Any CPU.Build.0 = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Signed|ARM.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Signed|x64.ActiveCfg = Release|Any CPU {5AA504E2-10A2-4179-9645-47E5CF857DDF}.Signed|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {0D39CF6F-4884-4D43-8744-2CEB723629C8} = {2BDD39FE-2E9F-4B97-8173-02037CF765AA} {96B18D27-DFF3-47A2-835B-EF1E6AF1A39C} = {F52A1420-8C01-42ED-8EF2-71F83EE49A87} {6445515D-9E58-4812-BAD6-BC81DAF1C0EE} = {F52A1420-8C01-42ED-8EF2-71F83EE49A87} {FFD91CC6-07FF-4D69-A064-2A911E2B4087} = {8F22488F-603B-4BA2-9AA0-DBC7D4487978} {5AA504E2-10A2-4179-9645-47E5CF857DDF} = {F52A1420-8C01-42ED-8EF2-71F83EE49A87} EndGlobalSection EndGlobal ================================================ FILE: sdk/Javascript/package.json ================================================ { "name": "AzureMobileServices", "version": "1.2.8", "devDependencies": { "grunt": "~0.4.5", "grunt-contrib-jshint": "~0.10.0", "grunt-contrib-nodeunit": "~0.4.1", "grunt-contrib-uglify": "~0.6.0", "grunt-contrib-concat": "~0.5.0" } } ================================================ FILE: sdk/Javascript/src/Generated/.gitignore ================================================ * !.gitignore ================================================ FILE: sdk/Javascript/src/Internals/DevIntellisense.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- // The following declarations will let Visual Studio provide cross-module // Intellisense by defining free variables provided by the module system as // globals that it can find. Visual Studio's JavaScript Intellisense engine // will actually evaluate code on a background thread, so when your module // calls require('foo'), VS actually runs the real require code (since we make // it visible here) and will provide Intellisense for all of foo's exports. global.exports = {}; global.require = require; ================================================ FILE: sdk/Javascript/src/Internals/InternalsVisible.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- // Walk all of the registered modules and make all of their exports publicly // available for unit testing. This relies on both the global and $__modules__ // free variables exposed via our module system. // Cache all of the module definition functions before any of the modules have // been require-d. This allows us to reset the modules by reevaluating all of // the definition functions. var moduleCache = {}; var moduleName = null; for (moduleName in $__modules__) { moduleCache[moduleName] = $__modules__[moduleName]; } // Expose all of the exports for each module exposeModules(); function exposeModules() { /// /// Expose all of the exports for a module as a global member so they can /// be easily accessed for testing. /// /// /// Note that all modules will be require-d to gain access to their /// exported members. /// var moduleName = null; for (moduleName in $__modules__) { // We need to require the module which will force all of its exports to // be defined (or do nothing if they've already been require-d). require(moduleName); // Declare a new global variable with the module's names that will // contain all of its exports. global[moduleName] = $__modules__[moduleName]; } } global.resetModules = function () { /// /// Reset the modules by reevaluating the functions that provide their /// exports. /// // Reset $__modules__ to contain the original functions var moduleName = null; for (moduleName in $__modules__) { $__modules__[moduleName] = moduleCache[moduleName]; } // Re-require all of the modules which will reevaluate their functions exposeModules(); }; ================================================ FILE: sdk/Javascript/src/Internals/NodeExports.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- // The server-side node runtime wants to provide the same query syntax and we // want to reuse as much code as possible. This will bundle up the entire // library and add a single node.js export that translates queries into OData. global.Query = require('Query').Query; ================================================ FILE: sdk/Javascript/src/LoginUis/BrowserPopup.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- var PostMessageExchange = require('PostMessageExchange'); exports.supportsCurrentRuntime = function () { /// /// Determines whether or not this login UI is usable in the current runtime. /// return true; }; exports.login = function (startUri, endUri, callback) { /// /// Displays the login UI and calls back on completion /// // Tell the runtime which form of completion signal we are looking for, // and which origin should be allowed to receive the result (note that this // is validated against whitelist on the server; we are only supplying this // origin to indicate *which* of the whitelisted origins to use). var completionOrigin = PostMessageExchange.getOriginRoot(window.location.href), runtimeOrigin = PostMessageExchange.getOriginRoot(startUri), // IE does not support popup->opener postMessage calls, so we have to // route the message via an iframe useIntermediateIframe = window.navigator.userAgent.indexOf("MSIE") >= 0 || window.navigator.userAgent.indexOf("Trident") >= 0, intermediateIframe = useIntermediateIframe && createIntermediateIframeForLogin(runtimeOrigin, completionOrigin), completionType = useIntermediateIframe ? "iframe" : "postMessage"; startUri += startUri.indexOf('?') == -1 ? '?' : '&'; startUri += "completion_type=" + completionType + "&completion_origin=" + encodeURIComponent(completionOrigin); // Browsers don't allow postMessage to a file:// URL (except by setting origin to "*", which is unacceptable) // so abort the process early with an explanation in that case. if (!(completionOrigin && (completionOrigin.indexOf("http:") === 0 || completionOrigin.indexOf("https:") === 0))) { var error = "Login is only supported from http:// or https:// URLs. Please host your page in a web server."; callback(error, null); return; } var loginWindow = window.open(startUri, "_blank", "location=no"), complete = function(errorValue, oauthValue) { // Clean up event handlers, windows, frames, ... window.clearInterval(checkForWindowClosedInterval); loginWindow.close(); if (window.removeEventListener) { window.removeEventListener("message", handlePostMessage); } else { // For IE8 window.detachEvent("onmessage", handlePostMessage); } if (intermediateIframe) { intermediateIframe.parentNode.removeChild(intermediateIframe); } // Finally, notify the caller callback(errorValue, oauthValue); }, handlePostMessage = function(evt) { // Validate source var expectedSource = useIntermediateIframe ? intermediateIframe.contentWindow : loginWindow; if (evt.source !== expectedSource) { return; } // Parse message var envelope; try { envelope = JSON.parse(evt.data); } catch(ex) { // Not JSON - it's not for us. Ignore it and keep waiting for the next message. return; } // Process message only if it's for us if (envelope && envelope.type === "LoginCompleted" && (envelope.oauth || envelope.error)) { complete(envelope.error, envelope.oauth); } }, checkForWindowClosedInterval = window.setInterval(function() { // We can't directly catch any "onclose" event from the popup because it's usually on a different // origin, but in all the mainstream browsers we can poll for changes to its "closed" property if (loginWindow && loginWindow.closed === true) { complete("canceled", null); } }, 250); if (window.addEventListener) { window.addEventListener("message", handlePostMessage, false); } else { // For IE8 window.attachEvent("onmessage", handlePostMessage); } // Permit cancellation, e.g., if the app tries to login again while the popup is still open return { cancelCallback: function () { complete("canceled", null); return true; // Affirm that it was cancelled } }; }; function createIntermediateIframeForLogin(runtimeOrigin, completionOrigin) { var frame = document.createElement("iframe"); frame.name = "zumo-login-receiver"; // loginviaiframe.html specifically looks for this name frame.src = runtimeOrigin + "/crossdomain/loginreceiver?completion_origin=" + encodeURIComponent(completionOrigin); frame.setAttribute("width", 0); frame.setAttribute("height", 0); frame.style.display = "none"; document.body.appendChild(frame); return frame; } ================================================ FILE: sdk/Javascript/src/LoginUis/CordovaPopup.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- // Note: Cordova is PhoneGap. // This login UI implementation uses the InAppBrowser plugin, // to install the plugin use the following command // cordova plugin add org.apache.cordova.inappbrowser var requiredCordovaVersion = { major: 3, minor: 0 }; exports.supportsCurrentRuntime = function () { /// /// Determines whether or not this login UI is usable in the current runtime. /// // When running application inside of Ripple emulator, InAppBrowser functionality is not supported. // We should use Browser popup login method instead. return !!currentCordovaVersion() && !isRunUnderRippleEmulator(); }; exports.login = function (startUri, endUri, callback) { /// /// Displays the login UI and calls back on completion /// // Ensure it's a sufficiently new version of Cordova, and if not fail synchronously so that // the error message will show up in the browser console. var foundCordovaVersion = currentCordovaVersion(), message; if (!isSupportedCordovaVersion(foundCordovaVersion)) { message = "Not a supported version of Cordova. Detected: " + foundCordovaVersion + ". Required: " + requiredCordovaVersion.major + "." + requiredCordovaVersion.minor; throw new Error(message); } if (!hasInAppBrowser) { message = 'A required plugin: "org.apache.cordova.inappbrowser" was not detected.'; throw new Error(message); } // Initially we show a page with a spinner. This stays on screen until the login form has loaded. var redirectionScript = "", startPage = "data:text/html," + encodeURIComponent(getSpinnerMarkup() + redirectionScript); // iOS inAppBrowser issue requires this wrapping setTimeout(function () { var loginWindow = window.open(startPage, "_blank", "location=no"), flowHasFinished = false, loadEventHandler = function (evt) { if (!flowHasFinished && evt.url.indexOf(endUri) === 0) { flowHasFinished = true; setTimeout(function () { loginWindow.close(); }, 500); var result = parseOAuthResultFromDoneUrl(evt.url); callback(result.error, result.oAuthToken); } }; // Ideally we'd just use loadstart because it happens earlier, but it randomly skips // requests on iOS, so we have to listen for loadstop as well (which is reliable). loginWindow.addEventListener('loadstart', loadEventHandler); loginWindow.addEventListener('loadstop', loadEventHandler); loginWindow.addEventListener('exit', function (evt) { if (!flowHasFinished) { flowHasFinished = true; callback("UserCancelled", null); } }); }, 500); }; function isRunUnderRippleEmulator () { // Returns true when application runs under Ripple emulator return window.parent && !!window.parent.ripple; } function currentCordovaVersion() { // If running inside Cordova, returns a string similar to "3.5.0". Otherwise, returns a falsey value. // Note: We can only detect Cordova after its deviceready event has fired, so don't call login until then. return window.cordova && window.cordova.version; } function isSupportedCordovaVersion(version) { var versionParts = currentCordovaVersion().match(/^(\d+).(\d+)./); if (versionParts) { var major = Number(versionParts[1]), minor = Number(versionParts[2]), required = requiredCordovaVersion; return (major > required.major) || (major === required.major && minor >= required.minor); } return false; } function hasInAppBrowser() { return !window.open; } function parseOAuthResultFromDoneUrl(url) { var successMessage = extractMessageFromUrl(url, "#token="), errorMessage = extractMessageFromUrl(url, "#error="); return { oAuthToken: successMessage ? JSON.parse(successMessage) : null, error: errorMessage }; } function extractMessageFromUrl(url, separator) { var pos = url.indexOf(separator); return pos < 0 ? null : decodeURIComponent(url.substring(pos + separator.length)); } function getSpinnerMarkup() { // The default InAppBrowser experience isn't ideal, as it just shows the user a blank white screen // until the login form appears. This might take 10+ seconds during which it looks broken. // Also on iOS it's possible for the InAppBrowser to initially show the results of the *previous* // login flow if the InAppBrowser was dismissed before completion, which is totally undesirable. // To fix both of these problems, we display a simple "spinner" graphic via a data: URL until // the current login screen has loaded. We generate the spinner via CSS rather than referencing // an animated GIF just because this makes the client library smaller overall. var vendorPrefix = "webkitTransform" in document.documentElement.style ? "-webkit-" : "", numSpokes = 12, spokesMarkup = ""; for (var i = 0; i < numSpokes; i++) { spokesMarkup += "
"; } return [ "", "", "
" + spokesMarkup + "
", "", "" ].join("").replace(/-prefix-/g, vendorPrefix); } ================================================ FILE: sdk/Javascript/src/LoginUis/WebAuthBroker.js ================================================  exports.supportsCurrentRuntime = function () { /// /// Determines whether or not this login UI is usable in the current runtime. /// return isWebAuthBrokerAvailable(); }; exports.login = function (startUri, endUri, callback) { /// /// Displays the login UI and calls back on completion /// // Define shortcuts for namespaces var windowsWebAuthBroker = Windows.Security.Authentication.Web.WebAuthenticationBroker; var noneWebAuthOptions = Windows.Security.Authentication.Web.WebAuthenticationOptions.none; var successWebAuthStatus = Windows.Security.Authentication.Web.WebAuthenticationStatus.success; var activationKindWebAuthContinuation = Windows.ApplicationModel.Activation.ActivationKind.webAuthenticationBrokerContinuation; var webAuthBrokerSuccessCallback = null; var webAuthBrokerErrorCallback = null; var webAuthBrokerContinuationCallback = null; // define callbacks for WebAuthenticationBroker webAuthBrokerSuccessCallback = function (result) { var error = null; var token = null; if (result.responseStatus !== successWebAuthStatus) { error = result; } else { var callbackEndUri = result.responseData; var tokenAsJson = null; var i = callbackEndUri.indexOf('#token='); if (i > 0) { tokenAsJson = decodeURIComponent(callbackEndUri.substring(i + 7)); } else { i = callbackEndUri.indexOf('#error='); if (i > 0) { error = decodeURIComponent(callbackEndUri.substring(i + 7)); } } if (tokenAsJson !== null) { try { token = JSON.parse(tokenAsJson); } catch (e) { error = e; } } } callback(error, token); }; webAuthBrokerErrorCallback = function (error) { callback(error, null); }; // Continuation callback is used when we're running on WindowsPhone which uses // AuthenticateAndContinue method instead of AuthenticateAsync, which uses different async model // Continuation callback need to be assigned to Application's 'activated' event. webAuthBrokerContinuationCallback = function (activationArgs) { if (activationArgs.detail.kind === activationKindWebAuthContinuation) { var result = activationArgs.detail.webAuthenticationResult; if (result.responseStatus == successWebAuthStatus) { webAuthBrokerSuccessCallback(result); } else { webAuthBrokerErrorCallback(result); } WinJS.Application.removeEventListener('activated', webAuthBrokerContinuationCallback); } }; if (endUri) { var windowsStartUri = new Windows.Foundation.Uri(startUri); var windowsEndUri = new Windows.Foundation.Uri(endUri); // If authenticateAndContinue method is available, we should use it instead of authenticateAsync if (windowsWebAuthBroker.authenticateAndContinue) { WinJS.Application.addEventListener('activated', webAuthBrokerContinuationCallback, true); windowsWebAuthBroker.authenticateAndContinue(windowsStartUri, windowsEndUri); } else { windowsWebAuthBroker.authenticateAsync(noneWebAuthOptions, windowsStartUri, windowsEndUri) .done(webAuthBrokerSuccessCallback, webAuthBrokerErrorCallback); } } else { // If no endURI was given, then we'll use the single sign-on overload of the // windowsWebAuthBroker. Single sign-on requires that the application's Package SID // be registered with the Microsoft Azure Mobile Service, but it provides a better // experience as HTTP cookies are supported so that users do not have to // login in everytime the application is launched. var redirectUri = windowsWebAuthBroker.getCurrentApplicationCallbackUri().absoluteUri; var startUriWithRedirect = startUri + "?sso_end_uri=" + encodeURIComponent(redirectUri); var windowsStartUriWithRedirect = new Windows.Foundation.Uri(startUriWithRedirect); // If authenticateAndContinue method is available, we should use it instead of authenticateAsync if (windowsWebAuthBroker.authenticateAndContinue) { WinJS.Application.addEventListener('activated', webAuthBrokerContinuationCallback, true); windowsWebAuthBroker.authenticateAndContinue(windowsStartUriWithRedirect); } else { windowsWebAuthBroker.authenticateAsync(noneWebAuthOptions, windowsStartUriWithRedirect) .done(webAuthBrokerSuccessCallback, webAuthBrokerErrorCallback); } } }; function isWebAuthBrokerAvailable() { // If running on windows8/8.1 or Windows Phone returns true, otherwise false return !!(window.Windows && window.Windows.Security && window.Windows.Security.Authentication && window.Windows.Security.Authentication.Web && window.Windows.Security.Authentication.Web.WebAuthenticationBroker); } ================================================ FILE: sdk/Javascript/src/Microsoft.WindowsAzure.Mobile.JS.csproj ================================================  $(MSBuildProjectDirectory)\..\tools $(MSBuildProgramFiles32)\Windows Kits\8.0\bin\x64\makepri.exe ..\..\Tools\makepri.exe v4.5 Debug AnyCPU 8.0.30703 2.0 {0D39CF6F-4884-4D43-8744-2CEB723629C8} Library Properties Microsoft.WindowsAzure.Mobile.JS Microsoft.WindowsAzure.Mobile.JS en-US 512 {BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true full false bin\Debug\ DEBUG;TRACE;NETFX_CORE prompt 4 false ..\..\tools\Strict.ruleset true pdbonly true bin\Release\ TRACE;NETFX_CORE prompt 4 true bin\ARM\Debug\ DEBUG;TRACE;NETFX_CORE ;2008 full ARM false prompt ExpressRules.ruleset true bin\ARM\Release\ TRACE;NETFX_CORE true ;2008 pdbonly ARM false prompt ExpressRules.ruleset true true bin\x64\Debug\ DEBUG;TRACE;NETFX_CORE ;2008 full x64 false prompt ExpressRules.ruleset true bin\x64\Release\ TRACE;NETFX_CORE true ;2008 pdbonly x64 false prompt ExpressRules.ruleset true true bin\x86\Debug\ DEBUG;TRACE;NETFX_CORE ;2008 full x86 false prompt ExpressRules.ruleset true bin\x86\Release\ TRACE;NETFX_CORE true ;2008 pdbonly x86 false prompt ExpressRules.ruleset true true bin\BuildTools\ DEBUG;TRACE;NETFX_CORE true full AnyCPU false prompt ..\..\tools\Strict.ruleset true bin\ARM\BuildTools\ DEBUG;TRACE;NETFX_CORE ;2008 true full ARM false prompt ExpressRules.ruleset true true bin\x64\BuildTools\ DEBUG;TRACE;NETFX_CORE ;2008 true full x64 false prompt ExpressRules.ruleset true true bin\x86\BuildTools\ DEBUG;TRACE;NETFX_CORE ;2008 true full x86 false prompt ExpressRules.ruleset true $(BuildDependsOn); CreatePriFile; ================================================ FILE: sdk/Javascript/src/MobileServiceClient.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var _ = require('Extensions'); var Validate = require('Validate'); var Platform = require('Platform'); var MobileServiceTable = require('MobileServiceTable').MobileServiceTable; var MobileServiceLogin = require('MobileServiceLogin').MobileServiceLogin; var Push; try { Push = require('Push').Push; } catch (e) { } var _zumoFeatures = { JsonApiCall: "AJ", // Custom API call, where the request body is serialized as JSON GenericApiCall: "AG", // Custom API call, where the request body is sent 'as-is' AdditionalQueryParameters: "QS", // Table or API call, where the caller passes additional query string parameters OptimisticConcurrency: "OC", // Table update / delete call, using Optimistic Concurrency (If-Match headers) TableRefreshCall: "RF", // Refresh table call TableReadRaw: "TR", // Table reads where the caller uses a raw query string to determine the items to be returned TableReadQuery: "TQ", // Table reads where the caller uses a function / query OM to determine the items to be returned }; var _zumoFeaturesHeaderName = "X-ZUMO-FEATURES"; function MobileServiceClient(applicationUrl, applicationKey) { /// /// Initializes a new instance of the MobileServiceClient class. /// /// /// The URL to the Mobile Services application. /// /// /// The Mobile Service application's key. /// Validate.isString(applicationUrl, 'applicationUrl'); Validate.notNullOrEmpty(applicationUrl, 'applicationUrl'); Validate.isString(applicationKey, 'applicationKey'); this.applicationUrl = applicationUrl; this.applicationKey = applicationKey || null; var sdkInfo = Platform.getSdkInfo(); var osInfo = Platform.getOperatingSystemInfo(); var sdkVersion = sdkInfo.fileVersion.split(".").slice(0, 2).join("."); this.version = "ZUMO/" + sdkVersion + " (lang=" + sdkInfo.language + "; " + "os=" + osInfo.name + "; " + "os_version=" + osInfo.version + "; " + "arch=" + osInfo.architecture + "; " + "version=" + sdkInfo.fileVersion + ")"; this.currentUser = null; this._serviceFilter = null; this._login = new MobileServiceLogin(this); this.getTable = function (tableName) { /// /// Gets a reference to a table and its data operations. /// /// The name of the table. /// A reference to the table. Validate.isString(tableName, 'tableName'); Validate.notNullOrEmpty(tableName, 'tableName'); return new MobileServiceTable(tableName, this); }; if (Push) { this.push = new Push(this); } } // Export the MobileServiceClient class exports.MobileServiceClient = MobileServiceClient; // Define the MobileServiceClient in a namespace (note: this has global effects // unless the platform we're using chooses to ignore it because exports are // good enough). Platform.addToMobileServicesClientNamespace({ MobileServiceClient: MobileServiceClient }); MobileServiceClient.prototype.withFilter = function (serviceFilter) { /// /// Create a new MobileServiceClient with a filter used to process all /// of its HTTP requests and responses. /// /// /// The filter to use on the service. The signature of a serviceFilter is /// function(request, next, callback) /// where /// next := function(request, callback) /// callback := function(error, response) /// /// /// A new MobileServiceClient whose HTTP requests and responses will be /// filtered as desired. /// /// /// The Mobile Services HTTP pipeline is a chain of filters composed /// together by giving each the next operation which it can invoke /// (zero, one, or many times as necessary). The default continuation /// of a brand new MobileServiceClient will just get the HTTP response /// for the corresponding request. Here's an example of a Handle /// implementation that will automatically retry a request that times /// out. /// function(req, next, callback) { /// next(req, function(err, rsp) { /// if (rsp.statusCode >= 400) { /// next(req, callback); /// } else { /// callback(err, rsp); /// } /// }); /// } /// Note that because these operations are asynchronous, this sample /// filter could end up actually making two HTTP requests before /// returning a response to the developer without the developer writing /// any special code to handle the situation. /// - /// Filters are composed just like standard function composition. If /// we had new MobileServiceClient().withFilter(F1).withFilter(F2) /// .withFilter(F3), it's conceptually equivalent to saying: /// var response = F3(F2(F1(next(request))); /// Validate.notNull(serviceFilter, 'serviceFilter'); // Clone the current instance var client = new MobileServiceClient(this.applicationUrl, this.applicationKey); client.currentUser = this.currentUser; // Chain the service filter with any existing filters var existingFilter = this._serviceFilter; client._serviceFilter = _.isNull(existingFilter) ? serviceFilter : function (req, next, callback) { // compose existingFilter with next so it can be used as the next // of the new serviceFilter var composed = function (req, callback) { existingFilter(req, next, callback); }; serviceFilter(req, composed, callback); }; return client; }; MobileServiceClient.prototype._request = function (method, uriFragment, content, ignoreFilters, headers, features, callback) { /// /// Perform a web request and include the standard Mobile Services headers. /// /// /// The HTTP method used to request the resource. /// /// /// URI of the resource to request (relative to the Mobile Services /// runtime). /// /// /// Optional content to send to the resource. /// /// /// Optional parameter to indicate if the client filters should be ignored /// and the request should be sent directly. Is false by default. /// /// /// Optional request headers /// /// /// Codes for features which are used in this request, sent to the server for telemetry. /// /// /// Handler that will be called on the response. /// // Account for absent optional arguments if (_.isNull(callback) && (typeof features === 'function')) { callback = features; features = null; } if (_.isNull(callback) && (typeof headers === 'function')) { callback = headers; headers = null; } if (_.isNull(callback) && (typeof ignoreFilters === 'function')) { callback = ignoreFilters; ignoreFilters = false; } if (_.isNull(callback) && (typeof content === 'function')) { callback = content; content = null; } Validate.isString(method, 'method'); Validate.notNullOrEmpty(method, 'method'); Validate.isString(uriFragment, 'uriFragment'); Validate.notNull(uriFragment, 'uriFragment'); Validate.notNull(callback, 'callback'); // Create the absolute URI var options = { type: method.toUpperCase() }; if (_.url.isAbsoluteUrl(uriFragment)) { options.url = uriFragment; } else { options.url = _.url.combinePathSegments(this.applicationUrl, uriFragment); } // Set MobileServices authentication, application, User-Agent and telemetry headers options.headers = {}; if (!_.isNull(headers)) { _.extend(options.headers, headers); } options.headers["X-ZUMO-INSTALLATION-ID"] = MobileServiceClient._applicationInstallationId; if (!_.isNullOrEmpty(this.applicationKey)) { options.headers["X-ZUMO-APPLICATION"] = this.applicationKey; } if (this.currentUser && !_.isNullOrEmpty(this.currentUser.mobileServiceAuthenticationToken)) { options.headers["X-ZUMO-AUTH"] = this.currentUser.mobileServiceAuthenticationToken; } if (!_.isNull(MobileServiceClient._userAgent)) { options.headers["User-Agent"] = MobileServiceClient._userAgent; } if (!_.isNullOrEmpty["X-ZUMO-VERSION"]) { options.headers["X-ZUMO-VERSION"] = this.version; } if (_.isNull(options.headers[_zumoFeaturesHeaderName]) && features && features.length) { options.headers[_zumoFeaturesHeaderName] = features.join(','); } // Add any content as JSON if (!_.isNull(content)) { if (!_.isString(content)) { options.data = _.toJson(content); } else { options.data = content; } if(!_.hasProperty(options.headers, ['Content-Type','content-type','CONTENT-TYPE','Content-type'])) { options.headers['Content-Type'] = 'application/json'; } } else { // options.data must be set to null if there is no content or the xhr object // will set the content-type to "application/text" for non-GET requests. options.data = null; } // Treat any >=400 status codes as errors. Also treat the status code 0 as // an error (which indicates a connection failure). var handler = function (error, response) { if (!_.isNull(error)) { error = _.createError(error); } else if (!_.isNull(response) && (response.status >= 400 || response.status === 0)) { error = _.createError(null, response); response = null; } callback(error, response); }; // Make the web request if (!_.isNull(this._serviceFilter) && !ignoreFilters) { this._serviceFilter(options, Platform.webRequest, handler); } else { Platform.webRequest(options, handler); } }; MobileServiceClient.prototype.loginWithOptions = Platform.async( function (provider, options, callback) { /// /// Log a user into a Mobile Services application given a provider name with /// given options. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', /// 'windowsazureactivedirectory' (can also use 'aad') /// or 'microsoftaccount'. /// /// /// Contains additional parameter information, valid values are: /// token: provider specific object with existing OAuth token to log in with /// useSingleSignOn: Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// parameters: Any additional provider specific query string parameters. /// /// /// Optional callback accepting (error, user) parameters. /// this._login.loginWithOptions(provider, options, callback); }); MobileServiceClient.prototype.login = Platform.async( function (provider, token, useSingleSignOn, callback) { /// /// Log a user into a Mobile Services application given a provider name and optional /// authentication token. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', /// 'windowsazureactivedirectory' (can also use 'aad') /// or 'microsoftaccount'. If no provider is specified, the 'token' parameter /// is considered a Microsoft Account authentication token. If a provider is specified, /// the 'token' parameter is considered a provider-specific authentication token. /// /// /// Optional, provider specific object with existing OAuth token to log in with. /// /// /// Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// /// /// Optional callback accepting (error, user) parameters. /// this._login.login(provider, token, useSingleSignOn, callback); }); MobileServiceClient.prototype.logout = function () { /// /// Log a user out of a Mobile Services application. /// this.currentUser = null; }; MobileServiceClient.prototype.invokeApi = Platform.async( function (apiName, options, callback) { /// /// Invokes the specified custom api and returns a response object. /// /// /// The custom api to invoke. /// /// /// Contains additional parameter information, valid values are: /// body: The body of the HTTP request. /// method: The HTTP method to use in the request, with the default being POST, /// parameters: Any additional query string parameters, /// headers: HTTP request headers, specified as an object. /// /// /// Optional callback accepting (error, results) parameters. /// Validate.isString(apiName, 'apiName'); // Account for absent optional arguments if (_.isNull(callback)) { if (typeof options === 'function') { callback = options; options = null; } } Validate.notNull(callback, 'callback'); var parameters, method, body, headers; if (!_.isNull(options)) { parameters = options.parameters; if (!_.isNull(parameters)) { Validate.isValidParametersObject(options.parameters); } method = options.method; body = options.body; headers = options.headers; } headers = headers || {}; if (_.isNull(method)) { method = "POST"; } // if not specified, default to return results in JSON format if (_.isNull(headers.accept)) { headers.accept = 'application/json'; } // Construct the URL var urlFragment = _.url.combinePathSegments("api", apiName); if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } var features = []; if (!_.isNullOrEmpty(body)) { features.push(_.isString(body) ? _zumoFeatures.GenericApiCall : _zumoFeatures.JsonApiCall); } if (!_.isNull(parameters)) { features.push(_zumoFeatures.AdditionalQueryParameters); } // Make the request this._request( method, urlFragment, body, null, headers, features, function (error, response) { if (!_.isNull(error)) { callback(error, null); } else { var contentType; if (typeof response.getResponseHeader !== 'undefined') { // (when not using IframeTransport, IE9) contentType = response.getResponseHeader('Content-Type'); } // If there was no header / can't get one, try json if (!contentType) { try { response.result = _.fromJson(response.responseText); } catch(e) { // Do nothing, since we don't know the content-type, failing may be ok } } else if (contentType.toLowerCase().indexOf('json') !== -1) { response.result = _.fromJson(response.responseText); } callback(null, response); } }); }); function getApplicationInstallationId() { /// /// Gets or creates the static application installation ID. /// /// /// The application installation ID. /// // Get or create a new installation ID that can be passed along on each // request to provide telemetry data var applicationInstallationId = null; // Check if the config settings exist var path = "MobileServices.Installation.config"; var contents = Platform.readSetting(path); if (!_.isNull(contents)) { // Parse the contents of the file as JSON and pull out the // application's installation ID. try { var config = _.fromJson(contents); applicationInstallationId = config.applicationInstallationId; } catch (ex) { // Ignore any failures (like invalid JSON, etc.) which will allow // us to fall through to and regenerate a valid config below } } // If no installation ID was found, generate a new one and save the config // settings. This is pulled out as a separate function because we'll do it // even if we successfully read an existing config but there's no // installation ID. if (_.isNullOrEmpty(applicationInstallationId)) { applicationInstallationId = _.createUniqueInstallationId(); // TODO: How many other settings should we write out as well? var configText = _.toJson({ applicationInstallationId: applicationInstallationId }); Platform.writeSetting(path, configText); } return applicationInstallationId; } /// /// Get or set the static _applicationInstallationId by checking the settings /// and create the value if necessary. /// MobileServiceClient._applicationInstallationId = getApplicationInstallationId(); /// /// Get or set the static _userAgent by calling into the Platform. /// MobileServiceClient._userAgent = Platform.getUserAgent(); /// /// The features that are sent to the server for telemetry. /// MobileServiceClient._zumoFeatures = _zumoFeatures; ================================================ FILE: sdk/Javascript/src/MobileServiceLogin.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var _ = require('Extensions'); var Validate = require('Validate'); var Platform = require('Platform'); var loginUrl = "login"; var loginDone = "done"; function MobileServiceLogin(client, ignoreFilters) { /// /// Initializes a new instance of the MobileServiceLogin class. /// /// /// Reference to the MobileServiceClient associated with this login. /// /// /// Optional parameter to indicate if the client filters should be ignored /// and requests should be sent directly. Is true by default. This should /// only be set to false for testing purposes when filters are needed to intercept /// and validate requests and responses. /// // Account for absent optional arguments if (_.isNull(ignoreFilters)) { ignoreFilters = true; } // Validate arguments Validate.notNull(client); Validate.isObject(client, 'client'); // Create read/write fields this._loginState = { inProcess: false, cancelCallback: null }; this.ignoreFilters = ignoreFilters; // Create get accessors for read-only fields this.getMobileServiceClient = function () { /// /// Gets the MobileServiceClient associated with this table. /// /// /// The MobileServiceClient associated with this table. /// return client; }; this.getLoginInProcess = function () { /// /// Indicates if a login is currently in process or not. /// /// /// True if a login is in process and false otherwise. /// return this._loginState.inProcess; }; } // Export the MobileServiceLogin class exports.MobileServiceLogin = MobileServiceLogin; // Define the MobileServiceLogin in a namespace (note: this has global effects // unless the platform we're using chooses to ignore it because exports are // good enough). Platform.addToMobileServicesClientNamespace({ MobileServiceLogin: MobileServiceLogin }); MobileServiceLogin.prototype.loginWithOptions = function (provider, options, callback) { /// /// Log a user into a Mobile Services application given a provider name with /// given options. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', /// 'windowsazureactivedirectory' (can also use 'aad') /// or 'microsoftaccount'. /// /// /// Contains additional parameter information, valid values are: /// token: provider specific object with existing OAuth token to log in with /// useSingleSignOn: Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// parameters: Any additional provider specific query string parameters. /// /// /// Optional callback accepting (error, user) parameters. /// Validate.isString(provider, 'provider'); Validate.notNull(provider, 'provider'); if (_.isNull(callback)) { if (!_.isNull(options) && typeof options === 'function') { callback = options; options = null; } else { Validate.notNull(null, 'callback'); } } // loginWithOptions('a.b.c') if (!options && this._isAuthToken(provider)) { this.loginWithMobileServiceToken(provider, callback); } else { // loginWithOptions('facebook', {}); // loginWithOptions('facebook'); options = options || {}; this.loginWithProvider(provider, options.token, options.useSingleSignOn, options.parameters, callback); } }; MobileServiceLogin.prototype.login = function (provider, token, useSingleSignOn, callback) { /// /// Log a user into a Mobile Services application given a provider name and optional token object /// Microsoft Account authentication token. /// /// /// Optional name of the authentication provider to use; one of 'facebook', 'twitter', 'google', /// 'windowsazureactivedirectory' (can also use 'aad'), or 'microsoftaccount'. /// /// /// Optional provider specific object with existing OAuth token to log in with or /// a JWT Mobile Services authentication token if the provider is null. /// /// /// Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// /// /// Optional callback accepting (error, user) parameters. /// // Account for absent optional arguments if (_.isNull(callback)) { if (!_.isNull(useSingleSignOn) && (typeof useSingleSignOn === 'function')) { callback = useSingleSignOn; useSingleSignOn = null; } else if (!_.isNull(token) && (typeof token === 'function')) { callback = token; useSingleSignOn = null; token = null; } } if (_.isNull(useSingleSignOn)) { if (_.isBool(token)) { useSingleSignOn = token; token = null; } else { useSingleSignOn = false; } } // Determine if the provider is actually a Mobile Services authentication token if (_.isNull(token) && this._isAuthToken(provider)) { token = provider; provider = null; } // Validate parameters; there must be either a provider, a token or both if (_.isNull(provider)) { Validate.notNull(token); Validate.isString(token); } if (_.isNull(token)) { Validate.notNull(provider); Validate.isString(provider); provider = provider.toLowerCase(); } if (!_.isNull(provider)) { if (provider.toLowerCase() === 'windowsazureactivedirectory') { // The mobile service REST API uses '/login/aad' for Microsoft Azure Active Directory provider = 'aad'; } this.loginWithProvider(provider, token, useSingleSignOn, {}, callback); } else { this.loginWithMobileServiceToken(token, callback); } }; MobileServiceLogin.prototype._isAuthToken = function (value) { return value && _.isString(value) && value.split('.').length === 3; }; MobileServiceLogin.prototype.loginWithMobileServiceToken = function(authenticationToken, callback) { /// /// Log a user into a Mobile Services application given an Mobile Service authentication token. /// /// /// OAuth access token that authenticates the user. /// /// /// Optional callback accepting (error, user) parameters. /// var self = this; var client = self.getMobileServiceClient(); Validate.isString(authenticationToken, 'authenticationToken'); Validate.notNullOrEmpty(authenticationToken, 'authenticationToken'); client._request( 'POST', loginUrl, { authenticationToken: authenticationToken }, self.ignoreFilters, function(error, response) { onLoginResponse(error, response, client, callback); }); }; MobileServiceLogin.prototype.loginWithProvider = function(provider, token, useSingleSignOn, parameters, callback) { /// /// Log a user into a Mobile Services application given a provider name and optional token object. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', /// 'windowsazureactivedirectory' (can also use 'aad'), or 'microsoftaccount'. /// /// /// Optional, provider specific object with existing OAuth token to log in with. /// /// /// Optional, indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, but it /// provides a better experience as HTTP cookies are supported so that users do not have to /// login in everytime the application is launched. Is false be default. /// /// /// Any additional provider specific query string parameters. /// /// /// The callback to execute when the login completes: callback(error, user). /// // Validate arguments Validate.isString(provider, 'provider'); if (!_.isNull(token)) { Validate.isObject(token, 'token'); } // Throw if a login is already in process and is not cancellable if (this._loginState.inProcess) { var didCancel = this._loginState.cancelCallback && this._loginState.cancelCallback(); if (!didCancel) { throw Platform.getResourceString("MobileServiceLogin_LoginErrorResponse"); } } provider = provider.toLowerCase(); // Either login with the token or the platform specific login control. if (!_.isNull(token)) { loginWithProviderAndToken(this, provider, token, parameters, callback); } else { loginWithLoginControl(this, provider, useSingleSignOn, parameters, callback); } }; function onLoginComplete(error, token, client, callback) { /// /// Handles the completion of the login and calls the user's callback with /// either a user or an error. /// /// /// Optional error that may have occurred during login. Will be null if the /// login succeeded and their is a token. /// /// /// Optional token that represents the logged-in user. Will be null if the /// login failed and their is an error. /// /// /// The Mobile Service client associated with the login. /// /// /// The callback to execute when the login completes: callback(error, user). /// var user = null; if (_.isNull(error)) { // Validate the token if (_.isNull(token) || !_.isObject(token) || !_.isObject(token.user) || !_.isString(token.authenticationToken)) { error = Platform.getResourceString("MobileServiceLogin_InvalidResponseFormat"); } else { // Set the current user on the client and return it in the callback client.currentUser = token.user; client.currentUser.mobileServiceAuthenticationToken = token.authenticationToken; user = client.currentUser; } } if (!_.isNull(callback)) { callback(error, user); } } function onLoginResponse(error, response, client, callback) { /// /// Handles the completion of the login HTTP call and calls the user's callback with /// either a user or an error. /// /// /// Optional error that may have occurred during login. Will be null if the /// login succeeded and their is a token. /// /// /// Optional HTTP login response from the Mobile Service. Will be null if the /// login failed and their is an error. /// /// /// The Mobile Service client associated with the login. /// /// /// The callback to execute when the login completes: callback(error, user). /// var mobileServiceToken = null; if (_.isNull(error)) { try { mobileServiceToken = _.fromJson(response.responseText); } catch (e) { error = e; } } onLoginComplete(error, mobileServiceToken, client, callback); } function loginWithProviderAndToken(login, provider, token, parameters, callback) { /// /// Log a user into a Mobile Services application given a provider name and token object. /// /// /// The login instance that holds the context used with the login process. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', or /// 'microsoftaccount'. The provider should already have been validated. /// /// /// Provider specific object with existing OAuth token to log in with. /// /// /// Any additional provider specific query string parameters. /// /// /// The callback to execute when the login completes: callback(error, user). /// var client = login.getMobileServiceClient(); // This design has always been problematic, because the operation can take arbitrarily // long and there is no way for the UI to cancel it. We should probably remove this // one-at-a-time restriction. login._loginState = { inProcess: true, cancelCallback: null }; var url = loginUrl + '/' + provider; if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); url = _.url.combinePathAndQuery(url, queryString); } // Invoke the POST endpoint to exchange provider-specific token for a // Microsoft Azure Mobile Services token client._request( 'POST', url, token, login.ignoreFilters, function (error, response) { login._loginState = { inProcess: false, cancelCallback: null }; onLoginResponse(error, response, client, callback); }); } function loginWithLoginControl(login, provider, useSingleSignOn, parameters, callback) { /// /// Log a user into a Mobile Services application using a platform specific /// login control that will present the user with the given provider's login web page. /// /// /// The login instance that holds the context used with the login process. /// /// /// Name of the authentication provider to use; one of 'facebook', 'twitter', 'google', or 'microsoftaccount'. /// /// /// Optional, indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, but it /// provides a better experience as HTTP cookies are supported so that users do not have to /// login in everytime the application is launched. Is false be default. /// /// /// Any additional provider specific query string parameters. /// /// /// The callback to execute when the login completes: callback(error, user). /// var client = login.getMobileServiceClient(); var startUri = _.url.combinePathSegments( client.applicationUrl, loginUrl, provider); var endUri = null; if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); startUri = _.url.combinePathAndQuery(startUri, queryString); } // If not single sign-on, then we need to construct a non-null end uri. if (!useSingleSignOn) { endUri = _.url.combinePathSegments( client.applicationUrl, loginUrl, loginDone); } login._loginState = { inProcess: true, cancelCallback: null }; // cancelCallback gets set below // Call the platform to launch the login control, capturing any // 'cancel' callback that it returns var platformResult = Platform.login( startUri, endUri, function (error, mobileServiceToken) { login._loginState = { inProcess: false, cancelCallback: null }; onLoginComplete(error, mobileServiceToken, client, callback); }); if (login._loginState.inProcess && platformResult && platformResult.cancelCallback) { login._loginState.cancelCallback = platformResult.cancelCallback; } } ================================================ FILE: sdk/Javascript/src/MobileServiceTable.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var _ = require('Extensions'); var Validate = require('Validate'); var Platform = require('Platform'); var Query = require('Query').Query; // Name of the reserved Mobile Services ID member. var idPropertyName = "id"; // The route separator used to denote the table in a uri like // .../{app}/collections/{coll}. var tableRouteSeperatorName = "tables"; var idNames = ["ID", "Id", "id", "iD"]; var nextLinkRegex = /^(.*?);\s*rel\s*=\s*(\w+)\s*$/; var MobileServiceSystemProperties = { None: 0, CreatedAt: 1, UpdatedAt: 2, Version: 4, All: 0xFFFF }; var MobileServiceSystemColumns = { CreatedAt: "__createdAt", UpdatedAt: "__updatedAt", Version: "__version" }; Platform.addToMobileServicesClientNamespace({ MobileServiceTable: { SystemProperties: MobileServiceSystemProperties } }); function MobileServiceTable(tableName, client) { /// /// Initializes a new instance of the MobileServiceTable class. /// /// /// Name of the table. /// /// /// The MobileServiceClient used to make requests. /// this.getTableName = function () { /// /// Gets the name of the table. /// /// The name of the table. return tableName; }; this.getMobileServiceClient = function () { /// /// Gets the MobileServiceClient associated with this table. /// /// /// The MobileServiceClient associated with this table. /// return client; }; this.systemProperties = 0; } // Export the MobileServiceTable class exports.MobileServiceTable = MobileServiceTable; // We have an internal _read method using callbacks since it's used by both // table.read(query) and query.read(). MobileServiceTable.prototype._read = function (query, parameters, callback) { /// /// Query a table. /// /// /// The query to execute. It can be null or undefined to get the entire /// collection. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the query is complete. /// // Account for absent optional arguments if (_.isNull(callback)) { if (_.isNull(parameters) && (typeof query === 'function')) { callback = query; query = null; parameters = null; } else if (typeof parameters === 'function') { callback = parameters; parameters = null; if (!_.isNull(query) && _.isObject(query)) { // This 'query' argument could be either the query or the user-defined // parameters object since both are optional. A query is either (a) a simple string // or (b) an Object with an toOData member. A user-defined parameters object is just // an Object. We need to detect which of these has been passed in here. if (!_.isString(query) && _.isNull(query.toOData)) { parameters = query; query = null; } } } } // Validate the arguments if (query && _.isString(query)) { Validate.notNullOrEmpty(query, 'query'); } if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters, 'parameters'); } Validate.notNull(callback, 'callback'); // Get the query string var tableName = this.getTableName(); var queryString = null; var projection = null; var features = []; if (_.isString(query)) { queryString = query; if (!_.isNullOrEmpty(query)) { features.push(WindowsAzure.MobileServiceClient._zumoFeatures.TableReadRaw); } } else if (_.isObject(query) && !_.isNull(query.toOData)) { if (query.getComponents) { features.push(WindowsAzure.MobileServiceClient._zumoFeatures.TableReadQuery); var components = query.getComponents(); projection = components.projection; if (components.table) { // If the query has a table name, make sure it's compatible with // the table executing the query if (tableName !== components.table) { var message = _.format(Platform.getResourceString("MobileServiceTable_ReadMismatchedQueryTables"), tableName, components.table); callback(_.createError(message), null); return; } // The oDataQuery will include the table name; we need to remove // because the url fragment already includes the table name. var oDataQuery = query.toOData(); queryString = oDataQuery.replace(new RegExp('^/' + components.table), ''); } } } addQueryParametersFeaturesIfApplicable(features, parameters); // Add any user-defined query string parameters parameters = addSystemProperties(parameters, this.systemProperties, queryString); if (!_.isNull(parameters)) { var userDefinedQueryString = _.url.getQueryString(parameters); if (!_.isNullOrEmpty(queryString)) { queryString += '&' + userDefinedQueryString; } else { queryString = userDefinedQueryString; } } // Construct the URL var urlFragment = queryString; if (!_.url.isAbsoluteUrl(urlFragment)) { urlFragment = _.url.combinePathSegments(tableRouteSeperatorName, tableName); if (!_.isNull(queryString)) { urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } } // Make the request this.getMobileServiceClient()._request( 'GET', urlFragment, null, false, null, features, function (error, response) { var values = null; if (_.isNull(error)) { // Parse the response values = _.fromJson(response.responseText); // If the values include the total count, we'll attach that // directly to the array if (values && !Array.isArray(values) && typeof values.count !== 'undefined' && typeof values.results !== 'undefined') { // Create a new total count property on the values array values.results.totalCount = values.count; values = values.results; } // If we have a projection function, apply it to each item // in the collection if (projection !== null) { var i = 0; for (i = 0; i < values.length; i++) { values[i] = projection.call(values[i]); } } // Grab link header when possible if (Array.isArray(values) && response.getResponseHeader && _.isNull(values.nextLink)) { try { var link = response.getResponseHeader('Link'); if (!_.isNullOrEmpty(link)) { var result = nextLinkRegex.exec(link); // Only add nextLink when relation is next if (result && result.length === 3 && result[2] == 'next') { values.nextLink = result[1]; } } } catch (ex) { // If cors doesn't allow us to access the Link header // Just continue on without it } } } callback(error, values); }); }; MobileServiceTable.prototype.read = Platform.async(MobileServiceTable.prototype._read); MobileServiceTable.prototype.insert = Platform.async( function (instance, parameters, callback) { /// /// Insert a new object into a table. /// /// /// The instance to insert into the table. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the insert is complete. /// // Account for absent optional arguments if (_.isNull(callback) && (typeof parameters === 'function')) { callback = parameters; parameters = null; } // Validate the arguments Validate.notNull(instance, 'instance'); if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters); } Validate.notNull(callback, 'callback'); // Integer Ids can not have any Id set for (var i in idNames) { var id = instance[idNames[i]]; if (!_.isNullOrZero(id)) { if (_.isString(id)) { // String Id's are allowed iif using 'id' if (idNames[i] !== idPropertyName) { throw _.format( Platform.getResourceString("MobileServiceTable_InsertIdAlreadySet"), idPropertyName); } else { Validate.isValidId(id, idPropertyName); } } else { throw _.format( Platform.getResourceString("MobileServiceTable_InsertIdAlreadySet"), idPropertyName); } } } var features = addQueryParametersFeaturesIfApplicable([], parameters); // Construct the URL var urlFragment = _.url.combinePathSegments(tableRouteSeperatorName, this.getTableName()); parameters = addSystemProperties(parameters, this.systemProperties); if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } // Make the request this.getMobileServiceClient()._request( 'POST', urlFragment, instance, false, null, features, function (error, response) { if (!_.isNull(error)) { callback(error, null); } else { var result = getItemFromResponse(response); result = Platform.allowPlatformToMutateOriginal(instance, result); callback(null, result); } }); }); MobileServiceTable.prototype.update = Platform.async( function (instance, parameters, callback) { /// /// Update an object in a given table. /// /// /// The instance to update in the table. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the update is complete. /// var version, headers = {}, features = [], serverInstance; // Account for absent optional arguments if (_.isNull(callback) && (typeof parameters === 'function')) { callback = parameters; parameters = null; } // Validate the arguments Validate.notNull(instance, 'instance'); Validate.isValidId(instance[idPropertyName], 'instance.' + idPropertyName); if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters, 'parameters'); } Validate.notNull(callback, 'callback'); if (_.isString(instance[idPropertyName])) { version = instance.__version; serverInstance = removeSystemProperties(instance); } else { serverInstance = instance; } if (!_.isNullOrEmpty(version)) { headers['If-Match'] = getEtagFromVersion(version); features.push(WindowsAzure.MobileServiceClient._zumoFeatures.OptimisticConcurrency); } features = addQueryParametersFeaturesIfApplicable(features, parameters); parameters = addSystemProperties(parameters, this.systemProperties); // Construct the URL var urlFragment = _.url.combinePathSegments( tableRouteSeperatorName, this.getTableName(), encodeURIComponent(instance[idPropertyName].toString())); if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } // Make the request this.getMobileServiceClient()._request( 'PATCH', urlFragment, serverInstance, false, headers, features, function (error, response) { if (!_.isNull(error)) { setServerItemIfPreconditionFailed(error); callback(error); } else { var result = getItemFromResponse(response); result = Platform.allowPlatformToMutateOriginal(instance, result); callback(null, result); } }); }); MobileServiceTable.prototype.refresh = Platform.async( function (instance, parameters, callback) { /// /// Refresh the current instance with the latest values from the /// table. /// /// /// The instance to refresh. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the refresh is complete. /// // Account for absent optional arguments if (_.isNull(callback) && (typeof parameters === 'function')) { callback = parameters; parameters = null; } // Validate the arguments Validate.notNull(instance, 'instance'); if (!_.isValidId(instance[idPropertyName], idPropertyName)) { if (typeof instance[idPropertyName] === 'string' && instance[idPropertyName] !== '') { throw _.format(Platform.getResourceString("Validate_InvalidId"), idPropertyName); } else { callback(null, instance); } return; } if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters, 'parameters'); } Validate.notNull(callback, 'callback'); // Construct the URL var urlFragment = _.url.combinePathSegments( tableRouteSeperatorName, this.getTableName()); if (typeof instance[idPropertyName] === 'string') { var id = encodeURIComponent(instance[idPropertyName]).replace(/\'/g, '%27%27'); urlFragment = _.url.combinePathAndQuery(urlFragment, "?$filter=id eq '" + id + "'"); } else { urlFragment = _.url.combinePathAndQuery(urlFragment, "?$filter=id eq " + encodeURIComponent(instance[idPropertyName].toString())); } if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } var features = [WindowsAzure.MobileServiceClient._zumoFeatures.TableRefreshCall]; features = addQueryParametersFeaturesIfApplicable(features, parameters); // Make the request this.getMobileServiceClient()._request( 'GET', urlFragment, instance, false, null, features, function (error, response) { if (!_.isNull(error)) { callback(error, null); } else { var result = _.fromJson(response.responseText); if (Array.isArray(result)) { result = result[0]; //get first object from array } if (!result) { var message =_.format( Platform.getResourceString("MobileServiceTable_NotSingleObject"), idPropertyName); callback(_.createError(message), null); } result = Platform.allowPlatformToMutateOriginal(instance, result); callback(null, result); } }); }); MobileServiceTable.prototype.lookup = Platform.async( function (id, parameters, callback) { /// /// Gets an instance from a given table. /// /// /// The id of the instance to get from the table. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the lookup is complete. /// // Account for absent optional arguments if (_.isNull(callback) && (typeof parameters === 'function')) { callback = parameters; parameters = null; } // Validate the arguments Validate.isValidId(id, idPropertyName); if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters); } Validate.notNull(callback, 'callback'); // Construct the URL var urlFragment = _.url.combinePathSegments( tableRouteSeperatorName, this.getTableName(), encodeURIComponent(id.toString())); var features = addQueryParametersFeaturesIfApplicable([], parameters); parameters = addSystemProperties(parameters, this.systemProperties); if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } // Make the request this.getMobileServiceClient()._request( 'GET', urlFragment, null, false, null, features, function (error, response) { if (!_.isNull(error)) { callback(error, null); } else { var result = getItemFromResponse(response); callback(null, result); } }); }); MobileServiceTable.prototype.del = Platform.async( function (instance, parameters, callback) { /// /// Delete an object from a given table. /// /// /// The instance to delete from the table. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// /// The callback to invoke when the delete is complete. /// // Account for absent optional arguments if (_.isNull(callback) && (typeof parameters === 'function')) { callback = parameters; parameters = null; } // Validate the arguments Validate.notNull(instance, 'instance'); Validate.isValidId(instance[idPropertyName], 'instance.' + idPropertyName); Validate.notNull(callback, 'callback'); var headers = {}; var features = []; if (_.isString(instance[idPropertyName])) { if (!_.isNullOrEmpty(instance.__version)) { headers['If-Match'] = getEtagFromVersion(instance.__version); features.push(WindowsAzure.MobileServiceClient._zumoFeatures.OptimisticConcurrency); } } features = addQueryParametersFeaturesIfApplicable(features, parameters); parameters = addSystemProperties(parameters, this.systemProperties); if (!_.isNull(parameters)) { Validate.isValidParametersObject(parameters); } // Contruct the URL var urlFragment = _.url.combinePathSegments( tableRouteSeperatorName, this.getTableName(), encodeURIComponent(instance[idPropertyName].toString())); if (!_.isNull(parameters)) { var queryString = _.url.getQueryString(parameters); urlFragment = _.url.combinePathAndQuery(urlFragment, queryString); } // Make the request this.getMobileServiceClient()._request( 'DELETE', urlFragment, null, false, headers, features, function (error, response) { if (!_.isNull(error)) { setServerItemIfPreconditionFailed(error); } callback(error); }); }); // Copy select Query operators to MobileServiceTable so queries can be created // compactly. We'll just add them to the MobileServiceTable prototype and then // forward on directly to a new Query instance. var queryOperators = ['where', 'select', 'orderBy', 'orderByDescending', 'skip', 'take', 'includeTotalCount']; var copyOperator = function (operator) { MobileServiceTable.prototype[operator] = function () { /// /// Creates a new query. /// // Create a query associated with this table var table = this; var query = new Query(table.getTableName()); // Add a .read() method on the query which will execute the query. // This method is defined here per query instance because it's // implicitly tied to the table. query.read = Platform.async( function (parameters, callback) { /// /// Execute the query. /// table._read(query, parameters, callback); }); // Invoke the query operator on the newly created query return query[operator].apply(query, arguments); }; }; var i = 0; for (; i < queryOperators.length; i++) { // Avoid unintended closure capture copyOperator(queryOperators[i]); } // Table system properties function removeSystemProperties(instance) { var copy = {}; for(var property in instance) { if (property.substr(0, 2) !== '__') { copy[property] = instance[property]; } } return copy; } function addSystemProperties(parameters, properties, querystring) { if (properties === MobileServiceSystemProperties.None || (typeof querystring === 'string' && querystring.toLowerCase().indexOf('__systemproperties') >= 0)) { return parameters; } // Initialize an object if none passed in parameters = parameters || {}; // Don't override system properties if already set if(!_.isNull(parameters.__systemProperties)) { return parameters; } if (properties === MobileServiceSystemProperties.All) { parameters.__systemProperties = '*'; } else { var options = []; if (MobileServiceSystemProperties.CreatedAt & properties) { options.push(MobileServiceSystemColumns.CreatedAt); } if (MobileServiceSystemProperties.UpdatedAt & properties) { options.push(MobileServiceSystemColumns.UpdatedAt); } if (MobileServiceSystemProperties.Version & properties) { options.push(MobileServiceSystemColumns.Version); } parameters.__systemProperties = options.join(','); } return parameters; } // Add double quotes and unescape any internal quotes function getItemFromResponse(response) { var result = _.fromJson(response.responseText); if (response.getResponseHeader) { var eTag = response.getResponseHeader('ETag'); if (!_.isNullOrEmpty(eTag)) { result.__version = getVersionFromEtag(eTag); } } return result; } // Converts an error to precondition failed error function setServerItemIfPreconditionFailed(error) { if (error.request && error.request.status === 412) { error.serverInstance = _.fromJson(error.request.responseText); } } // Add wrapping double quotes and escape all double quotes function getEtagFromVersion(version) { var result = version.replace(/\"/g, '\\\"'); return "\"" + result + "\""; } // Remove surrounding double quotes and unescape internal quotes function getVersionFromEtag(etag) { var len = etag.length, result = etag; if (len > 1 && etag[0] === '"' && etag[len - 1] === '"') { result = etag.substr(1, len - 2); } return result.replace(/\\\"/g, '"'); } // Updates and returns the headers parameters with features used in the call function addQueryParametersFeaturesIfApplicable(features, userQueryParameters) { var hasQueryParameters = false; if (userQueryParameters) { if (Array.isArray(userQueryParameters)) { hasQueryParameters = userQueryParameters.length > 0; } else if (_.isObject(userQueryParameters)) { for (var k in userQueryParameters) { hasQueryParameters = true; break; } } } if (hasQueryParameters) { features.push(WindowsAzure.MobileServiceClient._zumoFeatures.AdditionalQueryParameters); } return features; } ================================================ FILE: sdk/Javascript/src/MobileServices.intellisense.js ================================================ // ---------------------------------------------------------------------------- //! Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- // Declare JSHint globals /*global WindowsAzure:false, intellisense:false */ intellisense.annotate(WindowsAzure.MobileServiceClient.prototype, { withFilter: function () { /// /// /// Create a new MobileServiceClient with a filter inserted into the http /// pipeline to process all of its HTTP requests and responses. /// /// /// The filter to use on the service. The signature of a serviceFilter is /// function(request, next, callback) where next := function(request, callback) /// and callback := function(error, response). /// /// /// A new MobileServiceClient whose HTTP requests and responses will be /// filtered as desired. /// /// }, login: function () { /// /// /// Logs a user into a mobile service by using the specified identity provider. /// /// /// The name of the identity provider, which instructs Mobile Services which provider to use for authentication. /// The following values are supported: 'facebook', 'twitter', 'google', 'windowsazureactivedirectory' (can also use 'aad') /// or 'microsoftaccount'. If no provider is specified, the 'token' parameter /// is considered a Microsoft Account authentication token. If a provider is specified, /// the 'token' parameter is considered a provider-specific authentication token. /// /// /// Optional JSON representation of an authentication token, which can be supplied when the client has already /// obtained a token from the identity provider. /// /// /// Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// /// /// A winJS.Promise object /// /// }, logout: function () { /// /// /// Logs a user out of a Mobile Services application. /// /// } }); intellisense.annotate(WindowsAzure, { MobileServiceClient: function () { /// /// /// Creates a new instance of the MobileServiceClient. /// /// /// The URL of the mobile service.. /// /// /// The application key of the mobile service.. /// /// }, MobileServiceTable: function () { /// /// /// Represents a table in a mobile service to support insert, update, delete, and query operations. /// /// } }); WindowsAzure.MobileServiceClient = (function () { var _client = WindowsAzure.MobileServiceClient; var wrapper = function () { var instance = new _client(); intellisense.annotate(instance, { /// The application key applicationKey: String, /// The current user currentUser: undefined, /// The application Url applicationUrl: String, getTable: function () { /// /// /// Gets a reference to a table and its data operations. /// /// The name of the table. /// A reference to the table. /// }, invokeApi: function () { /// /// /// Invokes the specified custom api and returns a response object. /// /// /// The custom api to invoke. /// /// /// Contains additional parameter information, valid values are: /// body: The body of the HTTP request. /// method: The HTTP method to use in the request, with the default being POST, /// parameters: Any additional query string parameters, /// headers: HTTP request headers, specified as an object. /// /// } }); instance.getTable = (function () { var _table = instance.getTable; var wrapper2 = function () { var instance2 = new _table(); intellisense.annotate(instance2, { del: function () { /// /// /// Deletes an object from a given table. /// /// The instance to delete from the table. /// A WinJS.Promise /// }, getMobileServiceClient: function () { /// /// /// The client associated with this table. /// /// A MobileServiceClient /// }, getTableName: function () { /// /// /// The name of the table. /// /// /// }, includeTotalCount: function () { /// /// /// Indicate that the query should include the total count for all the records returned /// ignoring any take paging limit clause specified. /// /// A query that can be further composed. /// }, insert: function () { /// /// /// Inserts data from the supplied JSON object into the table. /// /// /// The instance to insert into the table. It will be updated upon completion. /// /// A WinJS.Promise /// }, orderBy: function () { /// /// /// Sorts a query against the table by the selected columns, in ascending order. /// /// /// An array of the names of the columns to use for ordering /// /// A query that can be further composed. /// }, orderByDescending: function () { /// /// /// Sorts a query against the table by the selected columns, in descending order. /// /// /// An array of the names of the columns to use for ordering /// /// A query that can be further composed. /// }, read: function () { /// /// /// Executes a query against the table. /// /// /// The query to execute. When null or undefined, all rows are returned. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// A WinJS.Promise /// }, lookup: function () { /// /// /// Gets an instance from a given table. /// /// /// The id of the instance to get from the table. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// A WinJS.Promise /// }, refresh: function () { /// /// /// Refresh the current instance with the latest values from the table. /// /// /// The instance to refresh. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// A WinJS.Promise /// }, select: function () { /// /// /// Applies the specific column projection to the query against the table. /// /// /// Function that defines the projection. /// /// A query that can be further composed. /// }, skip: function () { /// /// /// Skips the specified number of rows in the query. /// /// /// The number of rows to skip when returning the result. /// /// A query that can be further composed. /// }, take: function () { /// /// /// Returns the specified number of rows in the query. /// /// /// The number of rows in the query to return. /// /// A query that can be further composed. /// }, update: function () { /// /// /// Updates an object in a given table. /// /// /// The instance to update in the table, as a JSON object. /// /// A WinJS.Promise /// }, where: function () { /// /// /// Applies a row filtering predicate to the query against the table. /// /// /// JSON object that defines the row filter. /// /// A query that can be further composed. /// } }); return instance2; }; intellisense.redirectDefinition(wrapper2, _table); return wrapper2; })(); return instance; }; intellisense.redirectDefinition(wrapper, _client); return wrapper; })(); ================================================ FILE: sdk/Javascript/src/MobileServices.intellisense.txt ================================================ // Declare JSHint globals /*global WindowsAzure:false, intellisense:false */ intellisense.annotate(WindowsAzure.MobileServiceClient.prototype, { withFilter: function () { /// /// /// Create a new MobileServiceClient with a filter inserted into the http /// pipeline to process all of its HTTP requests and responses. /// /// /// The filter to use on the service. The signature of a serviceFilter is /// function(request, next, callback) where next := function(request, callback) /// and callback := function(error, response). /// /// /// A new MobileServiceClient whose HTTP requests and responses will be /// filtered as desired. /// /// }, login: function () { /// /// /// Logs a user into a mobile service by using the specified identity provider. /// /// /// The name of the identity provider, which instructs Mobile Services which provider to use for authentication. /// The following values are supported: 'facebook', 'twitter', 'google', /// or 'microsoftaccount'. If no provider is specified, the 'token' parameter /// is considered a Microsoft Account authentication token. If a provider is specified, /// the 'token' parameter is considered a provider-specific authentication token. /// /// /// Optional JSON representation of an authentication token, which can be supplied when the client has already /// obtained a token from the identity provider. /// /// /// Only applies to Windows 8 clients. Will be ignored on other platforms. /// Indicates if single sign-on should be used. Single sign-on requires that the /// application's Package SID be registered with the Microsoft Azure Mobile Service, /// but it provides a better experience as HTTP cookies are supported so that users /// do not have to login in everytime the application is launched. /// /// /// A winJS.Promise object /// /// }, logout: function () { /// /// /// Logs a user out of a Mobile Services application. /// /// } }); intellisense.annotate(WindowsAzure, { MobileServiceClient: function () { /// /// /// Creates a new instance of the MobileServiceClient. /// /// /// The URL of the mobile service.. /// /// /// The application key of the mobile service.. /// /// PAV /// }, MobileServiceTable: function () { /// /// /// Represents a table in a mobile service to support insert, update, delete, and query operations. /// /// } }); WindowsAzure.MobileServiceClient = (function () { var _client = WindowsAzure.MobileServiceClient; var wrapper = function () { var instance = new _client(); intellisense.annotate(instance, { /// The application key applicationKey: String, /// The current user currentUser: undefined, /// The application Url applicationUrl: String, getTable: function () { /// /// /// Gets a reference to a table and its data operations. /// /// The name of the table. /// A reference to the table. /// } }); instance.getTable = (function () { var _table = instance.getTable; var wrapper2 = function () { var instance2 = new _table(); intellisense.annotate(instance2, { del: function () { /// /// /// Deletes an object from a given table. /// /// The instance to delete from the table. /// A WinJS.Promise /// }, getMobileServiceClient: function () { /// /// /// The client associated with this table. /// /// A MobileServiceClient /// }, getTableName: function () { /// /// /// The name of the table. /// /// /// }, includeTotalCount: function () { /// /// /// Indicate that the query should include the total count for all the records returned /// ignoring any take paging limit clause specified. /// /// A query that can be further composed. /// }, insert: function () { /// /// /// Inserts data from the supplied JSON object into the table. /// /// /// The instance to insert into the table. It will be updated upon completion. /// /// A WinJS.Promise /// }, orderBy: function () { /// /// /// Sorts a query against the table by the selected columns, in ascending order. /// /// /// The name of the first column to use for ordering /// /// /// The name of the second column to use for ordering /// /// A query that can be further composed. /// }, orderByDescending: function () { /// /// /// Inserts data from the supplied JSON object into the table. /// /// /// The name of the first column to use for ordering /// /// /// The name of the second column to use for ordering /// /// A query that can be further composed. /// }, read: function () { /// /// /// Executes a query against the table. /// /// /// The query to execute. When null or undefined, all rows are returned. /// /// /// An object of user-defined parameters and values to include in the request URI query string. /// /// A WinJS.Promise /// }, select: function () { /// /// /// Applies the specific column projection to the query against the table. /// /// /// Function that defines the projection. /// /// A query that can be further composed. /// }, skip: function () { /// /// /// Skips the specified number of rows in the query. /// /// /// The number of rows to skip when returning the result. /// /// A query that can be further composed. /// }, take: function () { /// /// /// Returns the specified number of rows in the query. /// /// /// The number of rows in the query to return. /// /// A query that can be further composed. /// }, update: function () { /// /// /// Updates an object in a given table. /// /// /// The instance to update in the table, as a JSON object. /// /// A WinJS.Promise /// }, where: function () { /// /// /// Applies a row filtering predicate to the query against the table. /// /// /// JSON object that defines the row filter. /// /// A query that can be further composed. /// } }); return instance2; }; intellisense.redirectDefinition(wrapper2, _table); return wrapper2; })(); return instance; }; intellisense.redirectDefinition(wrapper, _client); return wrapper; })(); ================================================ FILE: sdk/Javascript/src/MobileServices.priconfig.xml ================================================ ================================================ FILE: sdk/Javascript/src/Platforms/Platform.Web.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /*global $__fileVersion__:false, $__version__:false */ var _ = require('Extensions'); var Validate = require('Validate'); var Promises = require('Promises'); var Resources = require('Resources'); var inMemorySettingStore = {}; try { var key = '___z'; localStorage.setItem(key, key); localStorage.removeItem(key); inMemorySettingStore = localStorage; } catch (e) { // localStorage is not available } var bestAvailableTransport = null; var knownTransports = [ // In order of preference require('DirectAjaxTransport'), require('IframeTransport') ]; var knownLoginUis = [ // In order of preference require('WebAuthBroker'), require('CordovaPopup'), require('BrowserPopup') ]; // Matches an ISO date and separates out the fractional part of the seconds // because IE < 10 has quirks parsing fractional seconds var isoDateRegex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d*))?Z$/; // Feature-detect IE8's date serializer var dateSerializerOmitsDecimals = !JSON.stringify(new Date(100)).match(/\.100Z"$/); exports.async = function async(func) { /// /// Wrap a function that takes a callback into platform specific async /// operation (i.e., keep using callbacks or switch to promises). /// /// /// An async function with a callback as its last parameter /// /// /// Function that when invoked will return a promise. /// return function () { // Capture the context of the original call var that = this; var args = arguments; // Create a new promise that will wrap the async call return new Promises.Promise(function (complete, error) { // Add a callback to the args which will call the appropriate // promise handlers var callback = function (err) { if (_.isNull(err)) { // Call complete with all the args except for err complete.apply(null, Array.prototype.slice.call(arguments, 1)); } else { error(err); } }; Array.prototype.push.call(args, callback); try { // Invoke the async method which will in turn invoke our callback // which will in turn invoke the promise's handlers func.apply(that, args); } catch (ex) { // Thread any immediate errors like parameter validation // through the the callback callback(_.createError(ex)); } }); }; }; exports.addToMobileServicesClientNamespace = function (declarations) { /// /// Define a collection of declarations in the Mobile Services Client namespace. /// /// /// Object consisting of names and values to define in the namespace. /// // First ensure our 'WindowsAzure' namespace exists var namespaceObject = global.WindowsAzure = global.WindowsAzure || {}; // Now add each of the declarations to the namespace for (var key in declarations) { if (declarations.hasOwnProperty(key)) { namespaceObject[key] = declarations[key]; } } }; exports.readSetting = function readSetting(name) { /// /// Read a setting from a global configuration store. /// /// /// Name of the setting to read. /// /// /// The value of the setting or null if not set. /// return inMemorySettingStore[name]; }; exports.writeSetting = function writeSetting(name, value) { /// /// Write a setting to a global configuration store. /// /// /// Name of the setting to write. /// /// /// The value of the setting. /// inMemorySettingStore[name] = value; }; exports.webRequest = function (request, callback) { /// /// Make a web request. /// /// /// Object describing the request (in the WinJS.xhr format). /// /// /// The callback to execute when the request completes. /// return getBestTransport().performRequest(request, callback); }; exports.getUserAgent = function () { // Browsers don't allow you to set a custom user-agent in ajax requests. Trying to do so // will cause an exception. So we don't. return null; }; exports.getOperatingSystemInfo = function () { return { name: "--", version: "--", architecture: "--" }; }; exports.getSdkInfo = function () { var isCordovaEnvironment = window && window.cordova && window.cordova.version; return { language: isCordovaEnvironment ? "Cordova" : "Web", fileVersion: $__fileVersion__ }; }; exports.login = function (startUri, endUri, callback) { // Force logins to go over HTTPS because the runtime is hardcoded to redirect // the server flow back to HTTPS, and we need the origin to match. var findProtocol = /^[a-z]+:/, requiredProtocol = 'https:'; startUri = startUri.replace(findProtocol, requiredProtocol); endUri = endUri.replace(findProtocol, requiredProtocol); return getBestProvider(knownLoginUis).login(startUri, endUri, callback); }; exports.toJson = function (value) { /// /// Convert an object into JSON format. /// /// The value to convert. /// The value as JSON. // We're wrapping this so we can hook the process and perform custom JSON // conversions. Note that we don't have to add a special hook to correctly // serialize dates in ISO8061 because JSON.stringify does that by defualt. // TODO: Convert geolocations once they're supported // TODO: Expose the ability for developers to convert custom types return JSON.stringify(value, function (key, stringifiedValue) { if (dateSerializerOmitsDecimals && this && _.isDate(this[key])) { // IE8 doesn't include the decimal part in its serialization of dates // For consistency, we extract the non-decimal part from the string // representation, and then append the expected decimal part. var msec = this[key].getMilliseconds(), msecString = String(msec + 1000).substring(1); return stringifiedValue.replace(isoDateRegex, function (all, datetime) { return datetime + "." + msecString + "Z"; }); } else { return stringifiedValue; } }); }; exports.tryParseIsoDateString = function (text) { /// /// Try to parse an ISO date string. /// /// The text to parse. /// The parsed Date or null. Validate.isString(text); // Check against a lenient regex var matchedDate = isoDateRegex.exec(text); if (matchedDate) { // IE9 only handles precisely 0 or 3 decimal places when parsing ISO dates, // and IE8 doesn't parse them at all. Fortunately, all browsers can handle // 'yyyy/mm/dd hh:MM:ss UTC' (without fractional seconds), so we can rewrite // the date to that format, and the apply fractional seconds. var dateWithoutFraction = matchedDate[1], fraction = matchedDate[2] || "0", milliseconds = Math.round(1000 * Number("0." + fraction)); // 6 -> 600, 65 -> 650, etc. dateWithoutFraction = dateWithoutFraction .replace(/\-/g, "/") // yyyy-mm-ddThh:mm:ss -> yyyy/mm/ddThh:mm:ss .replace("T", " "); // yyyy/mm/ddThh:mm:ss -> yyyy/mm/dd hh:mm:ss // Try and parse - it will return NaN if invalid var ticks = Date.parse(dateWithoutFraction + " UTC"); if (!isNaN(ticks)) { return new Date(ticks + milliseconds); // ticks are just milliseconds since 1970/01/01 } } // Doesn't look like a date return null; }; exports.getResourceString = function (resourceName) { // For now, we'll just always use English return Resources["en-US"][resourceName]; }; exports.allowPlatformToMutateOriginal = function (original, updated) { // For the Web/HTML client, we don't modify the original object. // This is the more typical arrangement for most JavaScript data access. return updated; }; function getBestTransport() { // We cache this just because it gets called such a lot if (!bestAvailableTransport) { bestAvailableTransport = getBestProvider(knownTransports); } return bestAvailableTransport; } function getBestProvider(providers) { /// /// Given an array of objects which each have a 'supportsCurrentRuntime' function, /// returns the first instance where that function returns true. /// for (var i = 0; i < providers.length; i++) { if (providers[i].supportsCurrentRuntime()) { return providers[i]; } } throw new Error("Unsupported browser - no suitable providers are available."); } ================================================ FILE: sdk/Javascript/src/Platforms/Platform.WinJS.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// // Declare JSHint globals /*global WinJS:false, Windows:false, $__fileVersion__:false, $__version__:false */ var _ = require('Extensions'), Validate = require('Validate'), WebAuthBroker = require('WebAuthBroker'); exports.async = function async(func) { /// /// Wrap a function that takes a callback into platform specific async /// operation (i.e., keep using callbacks or switch to promises). /// /// /// An async function with a callback as its last parameter /// /// /// Function that when invoked will return a WinJS.Promise. /// return function () { // Capture the context of the original call var that = this; var args = arguments; // Create a new promise that will wrap the async call return new WinJS.Promise(function (complete, error) { // Add a callback to the args which will call the appropriate // promise handlers var callback = function (err) { if (_.isNull(err)) { // Call complete with all the args except for err complete.apply(null, Array.prototype.slice.call(arguments, 1)); } else { error(err); } }; Array.prototype.push.call(args, callback); try { // Invoke the async method which will in turn invoke our callback // which will in turn invoke the promise's handlers func.apply(that, args); } catch (ex) { // Thread any immediate errors like parameter validation // through the the callback callback(_.createError(ex)); } }); }; }; exports.addToMobileServicesClientNamespace = function (declarations) { /// /// Define a collection of declarations in the Mobile Services Client namespace. /// /// /// Object consisting of names and values to define in the namespace. /// try { WinJS.Namespace.define('WindowsAzure', declarations); } catch (ex) { // This can fail due to a WinRT issue where another assembly defining a // non-JavaScript type with a Microsoft namespace. The wrapper object // created to represent the namespace doesn't allow expando // properties... so it will never let any additional JavaScript // namespaces be defined under it. We only see this with our test // library at the moment, but it could also appear if customers are // using other Microsoft libraries in WinJS. } }; exports.readSetting = function readSetting(name) { /// /// Read a setting from a global configuration store. /// /// /// Name of the setting to read. /// /// /// The value of the setting or null if not set. /// var localSettings = Windows.Storage.ApplicationData.current.localSettings; return !_.isNull(localSettings) ? localSettings.values[name] : null; }; exports.writeSetting = function writeSetting(name, value) { /// /// Write a setting to a global configuration store. /// /// /// Name of the setting to write. /// /// /// The value of the setting. /// var localSettings = Windows.Storage.ApplicationData.current.localSettings; if (!_.isNull(localSettings)) { localSettings.values[name] = value; } }; exports.webRequest = function (request, callback) { /// /// Make a web request. /// /// /// Object describing the request (in the WinJS.xhr format). /// /// /// The callback to execute when the request completes. /// WinJS.xhr(request).done( function (response) { callback(null, response); }, function (error) { callback(null, error); }); }; exports.login = function (startUri, endUri, callback) { /// /// Log a user into a Mobile Services application by launching a /// browser-based control that will allow the user to enter their credentials /// with a given provider. /// /// /// The absolute URI to which the login control should first navigate to in order to /// start the login process flow. /// /// /// The absolute URI that indicates login is complete. Once the login control navigates /// to this URI, it will execute the callback. /// /// /// The callback to execute when the login completes: callback(error, endUri). /// // Account for absent optional arguments if (_.isNull(callback) && typeof endUri === 'function') { callback = endUri; endUri = null; } Validate.notNullOrEmpty(startUri, 'startUri'); Validate.isString(startUri, 'startUri'); WebAuthBroker.login(startUri, endUri, callback); }; exports.getOperatingSystemInfo = function () { var architecture = "Unknown"; // The Windows API provides the architecture as an enum, so we have to // lookup the string value var archValue = Windows.ApplicationModel.Package.current.id.architecture; switch (archValue) { case 0: architecture = "X86"; break; case 5: architecture = "Arm"; break; case 9: architecture = "X64"; break; case 11: architecture = "Neutral"; break; } return { name: "Windows 8", version: "--", architecture: architecture }; }; exports.getSdkInfo = function () { return { language: "WinJS", fileVersion: $__fileVersion__ }; }; exports.getUserAgent = function () { // The User-Agent header can not be set in WinJS return null; }; exports.toJson = function (value) { /// /// Convert an object into JSON format. /// /// The value to convert. /// The value as JSON. // We're wrapping this so we can hook the process and perform custom JSON // conversions. Note that we don't have to add a special hook to correctly // serialize dates in ISO8061 because JSON.stringify does that by defualt. // TODO: Convert geolocations once they're supported // TODO: Expose the ability for developers to convert custom types return JSON.stringify(value); }; exports.tryParseIsoDateString = function (text) { /// /// Try to parse an ISO date string. /// /// The text to parse. /// The parsed Date or null. Validate.isString(text); // Check against a lenient regex if (/^(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})(\.(\d{1,3}))?Z$/.test(text)) { // Try and parse - it will return NaN if invalid var ticks = Date.parse(text); if (!isNaN(ticks)) { // Convert to a regular Date return new Date(ticks); } } // Return null if not found return null; }; exports.getResourceString = function (resourceName) { var resourceManager = Windows.ApplicationModel.Resources.Core.ResourceManager.current; var resource = resourceManager.mainResourceMap.getValue("MobileServices/Resources/" + resourceName); return resource.valueAsString; }; exports.allowPlatformToMutateOriginal = function (original, updated) { /// /// Patch an object with the values returned by from the server. Given /// that it's possible for the server to change values on an insert/update, /// we want to make sure the client object reflects those changes. /// /// The original value. /// The updated value. /// The patched original object. if (!_.isNull(original) && !_.isNull(updated)) { var key = null; var binding = WinJS.Binding.as(original); for (key in updated) { if (key in original) { binding[key] = updated[key]; } else { binding.addProperty(key, updated[key]); } } // TODO: Should we also delete any fields on the original object that // aren't also on the updated object? Is that a scenario for scripts? } return original; }; ================================================ FILE: sdk/Javascript/src/Properties/AssemblyInfo.cs ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- using System; using System.Diagnostics.CodeAnalysis; using System.Resources; // NOTE: We're not actually using this project to generate a .NET class library // so these settings are more or less meaningless. We've just got this project // as there's no option for a "JavaScript Class Library" for Win8 projects. [assembly: SuppressMessage( "Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "Not an actual class library.")] [assembly: NeutralResourcesLanguageAttribute("en-US")] ================================================ FILE: sdk/Javascript/src/Push/LocalStorageManager.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// // Declare JSHint globals /*global WinJS:false, Windows:false */ var _ = require('Extensions'), Validate = require('Validate'), Platform = require('Platform'), Constants = { Version: 'v1.1.0', Keys: { Version: 'Version', PushHandle: 'ChannelUri', Registrations: 'Registrations', NativeRegistration: '$Default' }, }; function LocalStorageManager(storageKey) { this._registrations = {}; this._storageKey = 'MobileServices.Push.' + storageKey; this._isRefreshNeeded = false; Object.defineProperty(this, 'isRefreshNeeded', { get: function () { /// /// Gets a value indicating whether local storage data needs to be refreshed. /// return this._isRefreshNeeded; } }); this._pushHandle = null; Object.defineProperty(this, 'pushHandle', { get: function () { /// /// Gets the DeviceId of all registrations in the LocalStorageManager /// return _.isNull(this._pushHandle) ? '' : this._pushHandle; }, set: function (value) { Validate.notNullOrEmpty(value, 'pushHandle'); if (this._pushHandle !== value) { this._pushHandle = value; this._flushToSettings(); } } }); // Initialize our state this._initializeRegistrationInfoFromStorage(); } exports.LocalStorageManager = LocalStorageManager; LocalStorageManager.NativeRegistrationName = Constants.Keys.NativeRegistration; LocalStorageManager.prototype.getRegistrationIds = function () { /// /// Gets an array of all registration Ids /// /// /// An array of registration Ids in form of ['1','2','3'] /// var result = []; for (var name in this._registrations) { if (this._registrations.hasOwnProperty(name)) { result.push(this._registrations[name]); } } return result; }; LocalStorageManager.prototype.getRegistrationIdWithName = function (registrationName) { /// /// Get the registration Id from local storage /// /// /// The name of the registration mapping to search for /// /// /// The registration Id if it exists or null if it does not. /// Validate.notNullOrEmpty(registrationName, 'registrationName'); return this._registrations[registrationName]; }; LocalStorageManager.prototype.updateAllRegistrations = function (registrations, pushHandle) { /// /// Replace all registrations and the pushHandle with those passed in. /// /// /// An array of registrations to update. /// /// /// The pushHandle to update. /// Validate.notNull(pushHandle, 'pushHandle'); if (!registrations) { registrations = []; } this._registrations = {}; for (var i = 0; i < registrations.length; i++) { var name = registrations[i].templateName; if (_.isNullOrEmpty(name)) { name = Constants.Keys.NativeRegistration; } /// All registrations passed to this method will have registrationId as they /// come directly from notification hub where registrationId is the key field. this._registrations[name] = registrations[i].registrationId; } // Need to flush explictly as handle may not have changed this._pushHandle = pushHandle; this._flushToSettings(); this._isRefreshNeeded = false; }; LocalStorageManager.prototype.updateRegistrationWithName = function (registrationName, registrationId, pushHandle) { /// /// Update a registration mapping and the deviceId in local storage by registrationName /// /// /// The name of the registration mapping to update. /// /// /// The registrationId to update. /// /// /// The device Id to update the ILocalStorageManager to. /// Validate.notNullOrEmpty(registrationName, 'registrationName'); Validate.notNullOrEmpty(registrationId, 'registrationId'); Validate.notNullOrEmpty(pushHandle, 'pushHandle'); // TODO: We could check if the Id or Name has actually changed this._registrations[registrationName] = registrationId; this._pushHandle = pushHandle; this._flushToSettings(); }; LocalStorageManager.prototype.deleteRegistrationWithName = function (registrationName) { /// /// Delete a registration from local storage by name /// /// /// The name of the registration mapping to delete. /// Validate.notNullOrEmpty(registrationName, 'registrationName'); if (this._registrations.hasOwnProperty(registrationName)) { delete this._registrations[registrationName]; this._flushToSettings(); } }; LocalStorageManager.prototype.deleteAllRegistrations = function () { /// /// Clear all registrations from local storage. /// this._registrations = {}; this._flushToSettings(); }; // Private methods LocalStorageManager.prototype._flushToSettings = function () { /// /// Writes all registrations to storage /// var forStorage = {}; forStorage[Constants.Keys.Version] = Constants.Version; forStorage[Constants.Keys.PushHandle] = this._pushHandle; forStorage[Constants.Keys.Registrations] = this._registrations; Platform.writeSetting(this._storageKey, JSON.stringify(forStorage)); }; LocalStorageManager.prototype._initializeRegistrationInfoFromStorage = function () { /// /// Populates registration information from storage /// this._registrations = {}; try { // Read push handle var data = JSON.parse(Platform.readSetting(this._storageKey)); this._pushHandle = data[Constants.Keys.PushHandle]; if (!this._pushHandle) { this._isRefreshNeeded = true; return; } // Verify this.storage version var version = data[Constants.Keys.Version] || ''; this._isRefreshNeeded = (Constants.Version !== version.toLowerCase()); if (this._isRefreshNeeded) { return; } // read registrations this._registrations = data[Constants.Keys.Registrations]; } catch (err) { // It is possible that local storage is corrupted by users, bugs or other issues. // If this occurs, force a full refresh. this._isRefreshNeeded = true; } }; ================================================ FILE: sdk/Javascript/src/Push/Push.Web.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var _ = require('Extensions'), Validate = require('Validate'), Platform = require('Platform'), RegistrationManager = require('RegistrationManager').RegistrationManager, apns = function (push) { this._push = push; }, gcm = function (push) { this._push = push; }; function Push(mobileServicesClient) { this._apns = null; this._gcm = null; this._registrationManager = null; Object.defineProperties(this, { 'apns': { get: function () { if (!this._apns) { var name = _.format('MS-PushContainer-apns-{0}', mobileServicesClient.applicationUrl); this._registrationManager = new RegistrationManager(mobileServicesClient, 'apns', name); this._apns = new apns(this); } return this._apns; } }, 'gcm': { get: function () { if (!this._gcm) { var name = _.format('MS-PushContainer-gcm-{0}', mobileServicesClient.applicationUrl); this._registrationManager = new RegistrationManager(mobileServicesClient, 'gcm', name); this._gcm = new gcm(this); } return this._gcm; } } }); } exports.Push = Push; Push.prototype._register = function (platform, pushHandle, tags) { /// /// Register for native notification /// /// /// The deviceToken to register /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the nregister is completed /// var registration = makeCoreRegistration(pushHandle, platform, tags); return this._registrationManager.upsertRegistration(registration); }; Push.prototype._registerTemplate = function (platform, deviceToken, name, bodyTemplate, expiryTemplate, tags) { /// /// Register for template notification /// /// The deviceToken to register /// The name of the template /// /// The json body to register /// /// /// The json body to register /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the register is completed /// Validate.notNullOrEmpty(name, 'name'); Validate.notNullOrEmpty(bodyTemplate, 'bodyTemplate'); var templateAsString = bodyTemplate, registration = makeCoreRegistration(deviceToken, platform, tags); if (!_.isString(templateAsString)) { templateAsString = JSON.stringify(templateAsString); } registration.templateName = name; registration.templateBody = templateAsString; if (expiryTemplate) { registration.expiry = expiryTemplate; } return this._registrationManager.upsertRegistration(registration); }; Push.prototype._unregister = function (templateName) { /// /// Unregister for template notification /// /// /// The name of the template /// /// /// Promise that will complete when the unregister is completed /// Validate.notNullOrEmpty(templateName, 'templateName'); return this._registrationManager.deleteRegistrationWithName(templateName); }; Push.prototype._unregisterAll = function (pushHandle) { /// /// Unregister for all notifications /// /// /// The push handle to unregister everything for /// /// /// Promise that will complete when the unregister is completed /// Validate.notNullOrEmpty(pushHandle, 'pushHandle'); return this._registrationManager.deleteAllRegistrations(pushHandle); }; apns.prototype.registerNative = function (deviceToken, tags) { /// /// Register for native notification /// /// /// The deviceToken to register /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the register is completed /// return this._push._register('apns', deviceToken, tags); }; apns.prototype.registerTemplate = function (deviceToken, name, bodyTemplate, expiryTemplate, tags) { /// /// Register for template notification /// /// The deviceToken to register /// The name of the template /// /// String or json object defining the body of the template register /// /// /// String defining the datatime or template expresession that evaluates to a date time /// string to use for the expiry of the message /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the unregister is completed /// if (_.isNull(tags) && !_.isNull(expiryTemplate) && Array.isArray(expiryTemplate)) { tags = expiryTemplate; expiryTemplate = null; } return this._push._registerTemplate('apns', deviceToken, name, bodyTemplate, expiryTemplate, tags); }; apns.prototype.unregisterNative = function () { /// /// Unregister for native notification /// /// /// Promise that will complete when the unregister is completed /// return this._push._unregister(RegistrationManager.NativeRegistrationName); }; apns.prototype.unregisterTemplate = function (templateName) { /// /// Unregister for template notification /// /// /// The name of the template /// /// /// Promise that will complete when the unregister is completed /// return this._push._unregister(templateName); }; apns.prototype.unregisterAll = function (deviceToken) { /// /// DEBUG-ONLY: Unregisters all registrations for the given device token /// /// /// The device token /// /// /// Promise that will complete once all registrations are deleted /// return this._push._unregisterAll(deviceToken); }; gcm.prototype.registerNative = function (deviceId, tags) { /// /// Register for native notification /// /// /// The deviceToken to register /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the unregister is completed /// return this._push._register('gcm', deviceId, tags); }; gcm.prototype.registerTemplate = function (deviceId, name, bodyTemplate, tags) { /// /// Register for template notification /// /// /// The deviceId to register /// /// /// The name of the template /// /// /// String or json object defining the body to register /// /// /// Array containing the tags for this registeration /// /// /// Promise that will complete when the unregister is completed /// return this._push._registerTemplate('gcm', deviceId, name, bodyTemplate, null, tags); }; gcm.prototype.unregisterNative = function () { /// /// Unregister for native notification /// /// /// Promise that will complete when the register is completed /// return this._push._unregister(RegistrationManager.NativeRegistrationName); }; gcm.prototype.unregisterTemplate = function (templateName) { /// /// Unregister for template notification /// /// /// The name of the template /// /// /// Promise that will complete when the register is completed /// return this._push._unregister(templateName); }; gcm.prototype.unregisterAll = function (deviceId) { /// /// DEBUG-ONLY: Unregisters all registrations for the given device token /// /// /// The device id /// /// /// Promise that will complete once all registrations are deleted /// return this._push._unregisterAll(deviceId); }; function makeCoreRegistration(pushHandle, platform, tags) { Validate.notNullOrEmpty(pushHandle, 'pushHandle'); Validate.isString(pushHandle, 'pushHandle'); if (platform == 'apns') { pushHandle = pushHandle.toUpperCase(); } var registration = { platform: platform, deviceId: pushHandle }; if (tags) { Validate.isArray(tags, 'tags'); registration.tags = tags; } return registration; } ================================================ FILE: sdk/Javascript/src/Push/Push.WinJS.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var _ = require('Extensions'), Validate = require('Validate'), Platform = require('Platform'), RegistrationManager = require('RegistrationManager').RegistrationManager; function Push(mobileServicesClient, tileId) { tileId = tileId || '$Primary'; var packageName = Windows.ApplicationModel.Package.current.id.name, name = _.format('{0}-PushContainer-{1}-{2}', packageName, mobileServicesClient.applicationUrl, tileId); this._registrationManager = new RegistrationManager(mobileServicesClient, 'wns', name); } exports.Push = Push; Push.prototype.registerNative = function (channelUri, tags) { /// /// Register for native notification /// /// /// The channelUri to register /// /// /// Array containing the tags for this registeration /// /// /// A promise that will complete when the registration is complete. /// var registration = makeCoreRegistration(channelUri, tags); return this._registrationManager.upsertRegistration(registration); }; Push.prototype.registerTemplate = function (channelUri, templateBody, templateName, headers, tags) { /// /// Register for template notification /// /// The channelUri to register /// The xml body to register /// The name of the template /// Object containing key/value pairs for the template to provide to WNS. X-WNS-Type is required. Example: { 'X-WNS-Type' : 'wns/toast' } /// Array containing the tags for this registeration /// /// A promise that will complete when the registration is complete. /// var registration = makeCoreRegistration(channelUri, tags); if (templateBody) { Validate.isString(templateBody, 'templateBody'); registration.templateBody = templateBody; Validate.isString(templateName, 'templateName'); Validate.notNullOrEmpty(templateName); registration.templateName = templateName; if (headers) { Validate.isObject(headers); registration.headers = headers; } } return this._registrationManager.upsertRegistration(registration); }; Push.prototype.unregisterNative = function () { /// /// Unregister for native notification /// /// Promise that will complete when the unregister is completed return this.unregisterTemplate(RegistrationManager.NativeRegistrationName); }; Push.prototype.unregisterTemplate = function (templateName) { /// /// Unregister for template notification /// /// /// The name of the template /// /// Promise that will complete when the unregister is completed Validate.notNullOrEmpty(templateName, 'templateName'); return this._registrationManager.deleteRegistrationWithName(templateName); }; Push.prototype.unregisterAll = function (channelUri) { /// /// DEBUG-ONLY: Unregister all notifications for a specfic channelUri /// /// The channelUri to unregister /// Promise that will complete when the unregistration of all registrations at the channelUri is completed Validate.notNullOrEmpty(channelUri, 'channelUri'); return this._registrationManager.deleteAllRegistrations(channelUri); }; Push.prototype.getSecondaryTile = function (tileId) { // TODO: move below to function var packageName = Windows.ApplicationModel.Package.current.id.name, name = _.format('{0}-PushContainer-{1}-{2}', packageName, mobileServicesClient.applicationUrl, tileId), localStorage = new LocalStorageManager(name); return new Push(name); }; function makeCoreRegistration(channelUri, tags) { var registration = {}; registration.platform = 'wns'; Validate.isString(channelUri, 'channelUri'); Validate.notNullOrEmpty(channelUri, 'channelUri'); registration.deviceId = channelUri; if (tags) { Validate.isArray(tags, 'tags'); registration.tags = tags; } return registration; } ================================================ FILE: sdk/Javascript/src/Push/PushHttpClient.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// var Platform = require('Platform'), noCacheHeader = { 'If-Modified-Since': 'Mon, 27 Mar 1972 00:00:00 GMT' }; function PushHttpClient(mobileServicesClient) { this.mobileServicesClient = mobileServicesClient; } exports.PushHttpClient = PushHttpClient; PushHttpClient.prototype.listRegistrations = function (pushHandle, platform, callback) { this.mobileServicesClient._request( 'GET', '/push/registrations?platform=' + encodeURIComponent(platform) + '&deviceId=' + encodeURIComponent(pushHandle), null, null, noCacheHeader, function (error, request) { if (error) { callback(error); } else { callback(null, JSON.parse(request.responseText)); } }); }; PushHttpClient.prototype.unregister = function (registrationId, callback) { this.mobileServicesClient._request( 'DELETE', '/push/registrations/' + encodeURIComponent(registrationId), null, null, noCacheHeader, function (error) { if (error && error.request && error.request.status === 404) { callback(); return; } callback(error); }); }; PushHttpClient.prototype.createRegistrationId = function (callback) { this.mobileServicesClient._request( 'POST', '/push/registrationIds', null, null, noCacheHeader, function (error, request) { if (error) { callback(error); return; } var locationHeader = request.getResponseHeader('Location'); callback(null, locationHeader.slice(locationHeader.lastIndexOf('/') + 1)); }); }; PushHttpClient.prototype.upsertRegistration = function (registration, callback) { this.mobileServicesClient._request( 'PUT', '/push/registrations/' + encodeURIComponent(registration.registrationId), registration, null, noCacheHeader, callback); }; ================================================ FILE: sdk/Javascript/src/Push/RegistrationManager.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// /// /// // Declare JSHint globals /*global WinJS:false */ var _ = require('Extensions'), Validate = require('Validate'), Platform = require('Platform'), LocalStorageManager = require('LocalStorageManager').LocalStorageManager, PushHttpClient = require('PushHttpClient').PushHttpClient; function RegistrationManager(mobileServicesClient, platform, storageKey) { Validate.notNull(mobileServicesClient, 'mobileServicesClient'); this._platform = platform || 'wns'; this._pushHttpClient = new PushHttpClient(mobileServicesClient); this._storageManager = new LocalStorageManager(storageKey || mobileServicesClient.applicationUrl); } exports.RegistrationManager = RegistrationManager; RegistrationManager.NativeRegistrationName = LocalStorageManager.NativeRegistrationName; RegistrationManager.prototype.upsertRegistration = Platform.async( function (registration, finalCallback) { Validate.notNull(registration, 'registration'); Validate.notNull(finalCallback, 'callback'); var self = this, expiredRegistration = function (callback) { createRegistration(function (error) { if (error) { callback(error); return; } upsertRegistration(false, callback); }); }, upsertRegistration = function (retry, callback) { self._pushHttpClient.upsertRegistration(registration, function (error) { if (retry && error && error.request && error.request.status === 410) { expiredRegistration(callback); return; } else if (!error) { self._storageManager.pushHandle = registration.deviceId; } callback(error); }); }, createRegistration = function (callback) { self._pushHttpClient.createRegistrationId(function (error, registrationId) { if (error) { callback(error); return; } registration.registrationId = registrationId; self._storageManager.updateRegistrationWithName( registration.templateName || LocalStorageManager.NativeRegistrationName, registration.registrationId, registration.deviceId); callback(); }); }, firstRegistration = function (callback) { var name = registration.templateName || LocalStorageManager.NativeRegistrationName, cachedRegistrationId = self._storageManager.getRegistrationIdWithName(name); if (!_.isNullOrEmpty(cachedRegistrationId)) { registration.registrationId = cachedRegistrationId; upsertRegistration(true, callback); } else { createRegistration(function (error) { if (error) { callback(error); return; } upsertRegistration(true, callback); }); } }; if (this._storageManager.isRefreshNeeded) { // We want the existing handle to win (if present), and slowly update them to the new handle // So use cached value over the passed in value this._refreshRegistrations(this._storageManager.pushHandle || registration.deviceId, function (error) { if (error) { finalCallback(error); return; } firstRegistration(finalCallback); }); } else { firstRegistration(finalCallback); } }); RegistrationManager.prototype.deleteRegistrationWithName = Platform.async( function (registrationName, callback) { var cachedRegistrationId = this._storageManager.getRegistrationIdWithName(registrationName), self = this; if (_.isNullOrEmpty(cachedRegistrationId)) { callback(); return; } this._pushHttpClient.unregister(cachedRegistrationId, function (error) { if (!error) { self._storageManager.deleteRegistrationWithName(registrationName); } callback(error); }); }); RegistrationManager.prototype.deleteAllRegistrations = Platform.async( function (pushHandle, callback) { var self = this, currentHandle = this._storageManager.pushHandle, deleteRegistrations = function (error, deleteCallback) { if (error) { deleteCallback(error); return; } var registrationIds = self._storageManager.getRegistrationIds(), remaining = registrationIds.length, errors = []; if (remaining === 0) { self._storageManager.deleteAllRegistrations(); deleteCallback(); return; } registrationIds.map(function (registrationId) { self._pushHttpClient.unregister(registrationId, function (error) { remaining--; if (error) { errors.push(error); } if (remaining <= 0) { if (errors.length === 0) { self._storageManager.deleteAllRegistrations(); deleteCallback(); } else { deleteCallback(_.createError('Failed to delete registrations for ' + pushHandle)); } } }); }); }; Validate.notNull(pushHandle, 'pushHandle'); // Try to refresh with the local storage copy first, then if different use the requested one this._refreshRegistrations(currentHandle || pushHandle, function (error) { if (_.isNullOrEmpty(currentHandle) || pushHandle === currentHandle) { deleteRegistrations(error, callback); } else { // Delete the current handle's registrations deleteRegistrations(error, function (error) { // Now delete the current handle's registrations as well // This requires the deleteAllRegistrations() call to clear the cached handle self._refreshRegistrations(pushHandle, function (error) { deleteRegistrations(error, callback); }); }); } }); }); RegistrationManager.prototype.listRegistrations = Platform.async( function (pushHandle, callback) { /// /// Retrives a list of all registrations from the server /// Validate.notNullOrEmpty(pushHandle); this._pushHttpClient.listRegistrations(pushHandle, this._platform, callback); }); RegistrationManager.prototype._refreshRegistrations = function (pushHandle, callback) { /// /// Reloads all registrations for the pushHandle passed in /// var self = this; Validate.notNull(pushHandle, 'pushHandle'); Validate.notNull(callback, 'callback'); this._pushHttpClient.listRegistrations(pushHandle, this._platform, function (error, registrations) { if (!error) { self._storageManager.updateAllRegistrations(registrations, pushHandle); } callback(error); }); }; ================================================ FILE: sdk/Javascript/src/Require.js ================================================ /// /// Map module names to either their cached exports or a function which /// will define the module's exports when invoked. /// var $__modules__ = { }; function require(name) { /// /// Require a module's exports. /// /// /// The name of the module. Note that we don't support full CommonJS /// Module specification names here - we only allow the name of the /// module's file without any extension. /// /// /// The exports provided by the module. /// if (name && name.length > 2 && name[0] == '.' && name[1] == '/') { name = name.slice(2); } var existing = $__modules__[name]; if (typeof existing == 'function') { var exports = { }; $__modules__[name] = exports; existing(exports); return exports; } else if (typeof existing == 'object') { return existing; } else { throw 'Unknown module ' + name; } } ================================================ FILE: sdk/Javascript/src/Strings/en-US/Resources.resjson ================================================ { "Validate_NotNullError" : "{0} cannot be null.", "Validate_NotNullOrEmptyError" : "{0} cannot be null or empty.", "Validate_InvalidId" : "{0} is not valid.", "Validate_TypeCheckError" : "{0} is expected to be a value of type {1}, not {2}.", "Validate_LengthUnexpected" : "{0} is expected to have length {1}, not {2}.", "Validate_InvalidUserParameter" : "{0} contains an invalid user-defined query string parameter: {1}. User-defined query string parameters must not begin with a '$'.", "Extensions_DefaultErrorMessage" : "Unexpected failure.", "Extensions_ConnectionFailureMessage" : "Unexpected connection failure.", "MobileServiceTable_ReadMismatchedQueryTables" : "Cannot get the results of a query for table '{1}' via table '{0}'.", "MobileServiceTable_InsertIdAlreadySet" : "Cannot insert if the {0} member is already set.", "MobileServiceLogin_AuthenticationProviderNotSupported" : "Unsupported authentication provider name. Please specify one of {0}.", "MobileServiceLogin_LoginErrorResponse" : "Cannot start a login operation because login is already in progress.", "MobileServiceLogin_InvalidResponseFormat" : "Invalid format of the authentication response.", "MobileServiceLogin_InvalidProvider" : "The first parameter must be the name of the autentication provider or a Microsoft Account authentication token.", "MobileServiceTable_NotSingleObject" : "Could not get object from response {0}.", "Push_ConflictWithReservedName" : "Template name conflicts with reserved name '{0}'.", "Push_InvalidTemplateName" : "Template name can't contain ';' or ':'.", "Push_NotSupportedXMLFormatAsBodyTemplateWin8" : "The bodyTemplate is not in accepted XML format. The first node of the bodyTemplate should be Badge\/Tile\/Toast, except for the wns\/raw template, which need to be a valid XML.", "Push_BodyTemplateMustBeXml" : "Valid XML is required for any template without a raw header.", "Push_TagNoCommas" : "Tags must not contain ','." } ================================================ FILE: sdk/Javascript/src/Transports/DirectAjaxTransport.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// // This transport is for modern browsers - it uses XMLHttpRequest with Cross-Origin Resource Sharing (CORS) exports.name = "DirectAjaxTransport"; exports.supportsCurrentRuntime = function () { /// /// Determines whether or not this transport is usable in the current runtime. /// // Feature-detect support for CORS (for IE, it's in version 10+) return (typeof global.XMLHttpRequest !== "undefined") && ('withCredentials' in new global.XMLHttpRequest()); }; exports.performRequest = function (request, callback) { /// /// Make a web request. /// /// /// Object describing the request (in the WinJS.xhr format). /// /// /// The callback to execute when the request completes. /// var headers = request.headers || {}, url = request.url.replace(/#.*$/, ""), // Strip hash part of URL for consistency across browsers httpMethod = request.type ? request.type.toUpperCase() : "GET", xhr = new global.XMLHttpRequest(); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { callback(null, xhr); } }; xhr.open(httpMethod, url); for (var key in headers) { if (request.headers.hasOwnProperty(key)) { xhr.setRequestHeader(key, request.headers[key]); } } xhr.send(request.data); }; ================================================ FILE: sdk/Javascript/src/Transports/IframeTransport.js ================================================ // ---------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // ---------------------------------------------------------------------------- /// // This transport is for midlevel browsers (IE8-9) that don't support CORS but do support postMessage. // It creates an invisible