How to deliver app with prefilled realm-database - realm

I would like to deliver my app with already prefilled data in my realm database. Do I have to simply copy it to the documents directory or is there some other things to do?

Realm's documentation has a section on "Bundling a Realm with an App":
It’s common to seed an app with initial data, making it available to your users immediately on first launch. Here’s how to do this:
First, populate the realm. You should use the same data model as your final, shipping app to create a realm and populate it with the data you wish to bundle with your app. Since realm files are cross-platform, you can use an OS X app (see our JSONImport example) or your iOS app running in the simulator.
In the code where you’re generating this realm file, you should finish by making a compacted copy of the file (see -[RLMRealm writeCopyToPath:error:]). This will reduce the Realm’s file size, making your final app lighter to download for your users.
Drag the new compacted copy of your realm file to your final app’s Xcode Project Navigator.
Go to your app target’s build phases tab in Xcode and add the realm file to the “Copy Bundle Resources” build phase.
At this point, your bundled realm file will be accessible to your app. You can find its path by using [[NSBundle mainBundle] pathForResource:ofType:].
You can either create a read-only realm by calling [RLMRealm realmWithPath:readOnly:error:]. Or, if you’d like to create a writable realm file based on this initial data, you can copy the bundled file to your application’s Documents directory using [[NSFileManager defaultManager] copyItemAtPath:toPath:error:] and then construct your new realm by using [RLMRealm realmWithPath:].
You can refer to our migration sample app for an example of how to use a bundled realm file.

Pre filled Realm-database For Android
Put your realm database in res/raw folder
and execute following code in activity:
// Copying realm database
copyBundledRealmFile(this.getResources().openRawResource(R.raw.default0), "default0.realm");
RealmConfiguration config0 = new RealmConfiguration.Builder()
.name("default0.realm")
.build();
realm = Realm.getInstance(config0);
private String copyBundledRealmFile(InputStream inputStream, String outFileName) {
try {
File file = new File(this.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

A much easier way is just creating an ad-hoc function to be called just when there is no data on your realm model ("MyModel", in this example), at first app launch:
let realm = try! Realm()
lazy var data: Results<MyModel> = { self.realm.objects(MyModel.self) }()
func populateDefaultData() {
if yourdata.count == 0 {
try! realm.write() {
let defaultData = ["Data1", "Data2", "Data3"]
for data in defaultData {
let newData = MyModel()
newData.data = data
realm.add(newData)
}
}
data = realm.objects(MyModel.self)
}
}
override func viewDidLoad() {
super.viewDidLoad()
populateDefaultData()
}

Related

How to launch Apple wallet automatically for .pkpass file from Xamarin forms app

I have successfully created the .pkpass file and the api successfully returns the .pkpass file. In Xamarin forms I consume the API and try to add the .pkpass file into wallet but the wallet is not launching automatically.
The file which consumed from api via Xamarin app is working fine, there is no issue with the file. I have sent is as an email attachment and downloaded the attachment - the .pkpass file automatically opens with wallet app.
public async Task DigitalMembershipCardApple()
{
string accesstoken = _dataHelper.GetAccessTokenFromDBAsync().Result;
try
{
_oHttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accesstoken);
HttpResponseMessage response = await _oHttpClient.GetAsync(new Uri(Constants.Urls.DigitalMembershipCardApple + _dataHelper.GetPersonID()));
byte[] filebytes = await response.Content.ReadAsByteArrayAsync();
string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), Constants.CISIMembershipCardFields.FileDownloadName);
if (File.Exists(filePath))
{
File.Delete(filePath);
File.WriteAllBytes(filePath, filebytes);
}
else
{
File.WriteAllBytes(filePath, filebytes);
}
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(filePath, Constants.CISIMembershipCardFields.MimeTypeApple)
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
throw ex;
}
}
I have also used the Xamarin essential launcher but that did not help.
Much appreciate a quick help
//Called from Xamarin Forms, now on iOS Project,
public async void AddToWallet(byte[] passByteArray)
{
NSData nsdata = NSData.FromArray(passByteArray);
NSError err = new NSError(new NSString("42"), -42);
PKPass newPass = new PKPass(nsdata, out err);
PKAddPassesViewController pkapvc = new PKAddPassesViewController(newPass);
await UIApplication.SharedApplication.KeyWindow.RootViewController.
PresentViewControllerAsync(pkapvc, true);
}
I don't use Apple wallet before while when I read document of PassKit in Xamarin.iOS, in the Adding Passes into Wallet section, it says:
Passes can be added to Wallet in the following ways:
Conduit Apps – These do not manipulate passes directly, they simply load pass files and present the user with the option of adding them to Wallet.
Companion Apps – These are written by providers to distribute passes and offer additional functionality to browse or edit them. Xamarin.iOS applications have complete access to the PassKit API to create and manipulate passes. Passes can then be added to Wallet using the PKAddPassesViewController. This process is described in more detail in the Companion Applications section of this document.
Mail is Conduit Application, it recognizes attachment as a Pass so the wallet app opens automatically .
Your xamarin app is Companion App, passes can then be added to Wallet using the PKAddPassesViewController. Read the file in your app won't open the wallet app.
My idea is you can download the file and follow the steps here to open the wallet app by using dependency-service in iOS project.
I used a different approach that show a popup to add multiple passes or review them.
Call this using a dependency service:
public async void AddAppleWalletPass(byte[] passByteArray)
{
if (PKPassLibrary.IsAvailable)
{
var library = new PKPassLibrary();
var passes = library.GetPasses();
NSData nsdata = NSData.FromArray(passByteArray);
NSError err = new NSError(new NSString("42"), -42);
PKPass newPass = new PKPass(nsdata, out err);
PKPass[] pKPasses = new PKPass[] { newPass };
await library.AddPassesAsync(pKPasses);
}
else
{
new UIAlertView("Alert", "Wallet is not available!", null, "Ok", null).Show();
}
}

UWP PreLoaded SQLite

I am trying to copy a preloaded SQLite db into my UWP app. On the initial installation it copies the "test.db", but the size is 0 bytes and there are no tables or data. The original db is 1300 bytes with data and tables.
Another factoid...when I create the app Using Visual Studio 2017 and compile and run/debug the app it works fine, but when I sideload the appx file or download from the Windows Store the db is empty.
Here is the code that I am using:
Task task = CopyDatabase();
private async Task CopyDatabase()
{
bool isDatabaseExisting = false;
try
{
StorageFile storageFile = await ApplicationData.Current.LocalFolder.GetFileAsync("Express.db");
isDatabaseExisting = true;
}
catch
{
isDatabaseExisting = false;
}
if (!isDatabaseExisting)
{
StorageFile databaseFile = await Package.Current.InstalledLocation.GetFileAsync("Express.db");
await databaseFile.CopyAsync(ApplicationData.Current.LocalFolder, "Express.db", NameCollisionOption.ReplaceExisting);
}
}
I'm not getting any error messages.
Does the your database file deployed correctly to the target system?
To confirm it, see your deployed - "Package" - folder. Open command prompt with administrative previleges, and see the directory
c:\Program Files\WindowsApps\your-app-id
If your database file deployed successfully, you can see it in the directory. If not, you may need to change the deploy settings.
To deploy the file to target machine, you should set the property of the one as ...
'BuildAction=Contents'
'Copy to output directory'='Always Copy'
You can set it from solution explorer and right-click the your database file.
If you succeeded the deploying file, your code will copy your database file to app local folder.
c:\Users\YOUR-USER-ID\AppData\Local\Packages\YOUR-APP-ID\LocalState
First, you would need to use await for your CopyDatabase method.
Second, I suggest you call this method in MainPage_Loaded event handler instead of MainPage's Constructor.
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
gui = this; InitializeComponent();
await CopyDatabase();
DataSetup();
CreateNewChartButton.Visibility = Visibility.Collapsed;
SignInButton_Click(null, null);
}

Null response creating file using Google Drive .NET API

I am trying to upload a file onto my Drive using Google Drive .NET API v3. My code is below
static string[] Scopes = { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveMetadataReadonly,
DriveService.Scope.DriveReadonly,
DriveService.Scope.DriveScripts };
static string ApplicationName = "Drive API .NET Quickstart";
public ActionResult Index()
{
UserCredential credential;
using (var stream =
new FileStream("C:/Users/admin1/Documents/visual studio 2017/Projects/TryGoogleDrive/TryGoogleDrive/client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Debug.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
// List files.
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
Debug.WriteLine("Files:");
if (files != null && files.Count > 0)
{
foreach (var file in files)
{
Debug.WriteLine("{0} ({1})", file.Name, file.Id);
}
}
else
{
Debug.WriteLine("No files found.");
}
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Name = "report.csv",
MimeType = "text/csv",
};
FilesResource.CreateMediaUpload request;
using (var stream = new FileStream("C:/debugging/report.csv",
FileMode.Open))
{
request = service.Files.Create(
fileMetadata, stream, "text/csv");
request.Fields = "id";
request.Upload();
}
var response = request.ResponseBody;
Console.WriteLine("File ID: " + response.Id);
return View();
}
The problem I'm facing is that response is always null. I looked into it a bit further and found that the request returned a 403 resultCode. I also took a look at some other questions on SO this and this but neither were of any help.
Edit: I forgot to mention that the first part of the code is working correctly - it lists all the files in my Drive. Only the second part is not working (the upload file part)
string[] Scopes = { DriveService.Scope.Drive };
Change the Drive scope then delete the file token.json
in vs2017 you can see token.json file in token.json folder when client_secret.json file present.
Try to visit this post from ASP.NET forum.
The same idea as what you want to do in your app, since you are dealing with uploading a file in Google Drive using .net.
You may try to call rest api directly to achieve your requirement :
The quickstart from .net will help you to make requests from/to the Drive API.
Upload Files:
The Drive API allows you to upload file data when create or
updating a File resource.
You can send upload requests in any of the following ways:
Simple upload: uploadType=media. For quick transfer of a small file (5 MB or less). To perform a simple upload, refer to Performing
a Simple Upload.
Multipart upload: uploadType=multipart. For quick transfer of a small file (5 MB or less) and metadata describing the file, all in a
single request. To perform a multipart upload, refer to Performing a
Multipart Upload.
Resumable upload: uploadType=resumable. For more reliable transfer, especially important with large files. Resumable uploads are
a good choice for most applications, since they also work for small
files at the cost of one additional HTTP request per upload. To
perform a resumable upload, refer to Performing a Resumable
Upload.
You may try this code from the documentation on uploading sample file.
var fileMetadata = new File()
{
Name = "photo.jpg"
};
FilesResource.CreateMediaUpload request;
using (var stream = new System.IO.FileStream("files/photo.jpg",
System.IO.FileMode.Open))
{
request = driveService.Files.Create(
fileMetadata, stream, "image/jpeg");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
Console.WriteLine("File ID: " + file.Id);
You may check the errors you may encounter in this documentation.
Have a look at what request.Upload() returns. For me when I was having this issue it returned:
Insufficient Permission Errors [Message[Insufficient Permission] Location[ - ]
I changed my scope from DriveService.Scope.DriveReadonly to DriveService.Scope.Drive and I was in business.
Change static string[] Scopes = { DriveService.Scope.DriveReadonly }; to static string[] Scopes = { DriveService.Scope.Drive };.
After changes, take a look into token.json file and check does it change its scope from DriveReadonly to Drive.
If you are seeing DriveReadonly then delete the token.json file and run the application again.

OpenStack Rackspace Cloud Files .net SDK

I am trying to save an XML file to a non CDN Container from Sydney:
public void Save(XDocument document)
{
using (MemoryStream ms = new MemoryStream())
{
document.Save(ms);
ms.Position = 0;
RackspaceCloudIdentity identity = new RackspaceCloudIdentity { Username = "username", APIKey = "xxxxxxxxxxx", CloudInstance = CloudInstance.Default };
CloudFilesProvider provider = new CloudFilesProvider(identity);
provider.CreateObject("XMLFiles", ms, "xmlFile1.xml", region: "syd");
}
}
For a 1MB file, it takes about 50 seconds to upload (very long).
And, trying to download the file back, returns an empty result:
public void Read()
{
RackspaceCloudIdentity identity = new RackspaceCloudIdentity { Username = "username", APIKey = "xxxxxxxxxxx", CloudInstance = CloudInstance.Default };
CloudFilesProvider provider = new CloudFilesProvider(identity);
using (MemoryStream ms = new MemoryStream())
{
provider.GetObject("XMLFiles", "xmlFile1.xml", ms, region: "syd");
// ms.Length is 0
}
}
I am doing something wrong?
Ugh. I introduced this bug in commit 799f37c (first released in v1.1.3.0). I'm looking into the best workaround right now.
Edit: There is no workaround, but I filed issue #116 for the issue, and after the pull request for it is merged, we'll release version 1.1.3.1 of the library to correct the problem.
Are you able to access your control panel at mycloud.rackspace.com?
I used my control panel to upload an XML file, then used your code, above, to download the XML file. It worked fine.
I'm going now use the upload code you posted.
Just wanted you to know I'm looking into this.

Windows Phone 8 Hanging on GetFolderAsync and OpenStreamForReadAsync

I am making a windows phone 8 application. Part of this application requires state to be saved. I am saving it as a string of Json. If I open the application, save some data, exit the application and the load it again, it hangs on either GetFolderAsync or OpenStreamForReadAsync. It does not happen every time, but once it starts hanging, I have to kill the whole emulator and make a new one to start the application again.
I have even tried just making an empty file with no data in it and the problem still persistes.
Below is the code I am using to save and load the data. It does not matter where I call the data load whether it be on application start or on the form load it still breaks.
private async Task SaveLists()
{
//XmlSerializer serializer = new XmlSerializer(typeof(ListHolder));
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
// Create a new folder name DataFolder.
var dataFolder = await local.CreateFolderAsync("DataFolder",
CreationCollisionOption.OpenIfExists);
// Create a new file named DataFile.txt.
var file = await dataFolder.CreateFileAsync("Lists.json",
CreationCollisionOption.ReplaceExisting);
string json = JsonConvert.SerializeObject(Lists, Formatting.Indented);
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(json.ToCharArray());
using (var s = await file.OpenStreamForWriteAsync())
{
s.Write(fileBytes, 0, fileBytes.Length);
}
}
private async Task LoadLists()
{
// Get the local folder.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
if (local != null)
{
try
{
// Get the DataFolder folder.
var dataFolder = await local.GetFolderAsync("DataFolder");
// Get the file.
var files = dataFolder.GetFilesAsync();
var file = await dataFolder.OpenStreamForReadAsync("Lists.json");
string jsonString = "";
// Read the data.
using (StreamReader streamReader = new StreamReader(file))
{
jsonString = streamReader.ReadToEnd();
}
if (jsonString.Length > 0)
{
Lists = JsonConvert.DeserializeObject<List<ItemList>>(jsonString);
}
else
{
Lists = new List<ItemList>();
}
}
catch (Exception ex)
{
Lists = new List<ItemList>();
}
}
}
You are causing a deadlock by calling Result. I explain this deadlock on my blog and in a recent MSDN article. In summary, await will (by default) attempt to resume execution within a context (the current SynchronizationContext unless it is null, in which case it uses the current TaskScheduler).
In your case, the current SynchronizationContext is the UI context, which is only used by the UI thread. So when you block the UI thread by calling Result, the async method cannot schedule back to the UI thread to complete.

Resources