In order to set up a system on AWS where one can create and use user accounts from an iOS app, I recently followed this tutorial which uses AWSMobileClient, AWSAuthCore and AWSAuthUI.
I got up something working where I can create accounts and log in and out.
Now I would like to make use of DynamoDB to allow the user to store something. For that, I have tried to integrate DynamoDB code that I have working in another app. But obviously the two apps environment are not quite the same, so it does not work as I would like it to.
Here is the code for the DynamoDB data that I want to handle:
import Foundation
import AWSDynamoDB
#objcMembers
class DynamoDBData: AWSDynamoDBObjectModel, AWSDynamoDBModeling {
var _message,_timeStamp,_user: String?
class func dynamoDBTableName() -> String {
return "DynamoDBData"
}
class func hashKeyAttribute() -> String {
return "_timeStamp"
}
class func rangeKeyAttribute() -> String {
return "_user"
}
override class func jsonKeyPathsByPropertyKey() -> [AnyHashable: Any] {
return [
"_message" : "message",
"_timeStamp" : "timeStamp",
"_user" : "user"
]
}
}
And here is the code for where I try to save something to the DB and fail:
#objc func handleTap() {
print(#function)
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
// Create data object using the data model:
let dataBlock = DynamoDBData()
dataBlock?._message = "message-TEST"
dataBlock?._timeStamp = "timeStamp-TEST"
dataBlock?._user = "user-TEST"
// Save the new item:
dynamoDbObjectMapper.save(dataBlock!, completionHandler: {
(error: Error?) -> Void in
if let error = error {
print("Amazon DynamoDB Save Error: \(error)")
return
}
print("An item was saved.")
})
}
Finally, this is the message I get showing a problem when trying to save data:
Amazon DynamoDB Save Error: Error Domain=com.amazonaws.AWSDynamoDBErrorDomain Code=19 "(null)"
UserInfo={__type=com.amazonaws.dynamodb.v20120810#ResourceNotFoundException,
message=Requested resource not found}
Since I have created a table DynamoDBData on the AWS console, I do not really understand why it says "Requested resource not found", but I guess I must have done something wrong at some point.
In case this can be useful, here is what appears in the AWS console for the table DynamoDBData:
Table name DynamoDBData
Primary partition key _timeStamp (String)
Primary sort key _user (String)
Point-in-time recovery DISABLED Enable
Encryption Type DEFAULT Manage Encryption
KMS Master Key ARN Not Applicable
Time to live attribute DISABLED Manage TTL
Table status Active
Creation date May 15, 2019 at 10:13:43 AM UTC+9
UTC: May 15, 2019 at 1:13:43 AM UTC
Local: May 15, 2019 at 10:13:43 AM UTC+9
Region (N. Virginia): May 14, 2019 at 8:13:43 PM UTC-5
Read/write capacity mode Provisioned
Last change to on-demand mode -
Provisioned read capacity units 5 (Auto Scaling Disabled)
Provisioned write capacity units 5 (Auto Scaling Disabled)
Last decrease time -
Last increase time -Storage size (in bytes) 0 bytes
Item count 0 Manage live count
Region US East (N. Virginia)
Amazon Resource Name (ARN) arn:aws:dynamodb:us-east-1:123456789012:table/DynamoDBData
Some guidance (even partial) on how to solve this issue would be very helpful.
One thing to double check: is the table's region the same region you're trying to access it in?
I noticed from a comment in your other question that the code was attempting to reach arn:aws:dynamodb:ap-northeast-1 but the above shows the table is actually in us-east-1.
Related
I am trying to read Personal Details (Blood group, Age, Gender) of Healthkit but unable to request for that.
As per Apple Doc here:
HealthKit provides five characteristic types: biological sex, blood
type, birthdate, Fitzpatrick skin type, and wheelchair use. These
types are used only when asking for permission to read data from the
HealthKit store.
But i can't make add HKCharacteristicType in authorisation request.
I have run Apple Sample Project which requests for:
HKQuantityTypeIdentifier.stepCount.rawValue,
HKQuantityTypeIdentifier.distanceWalkingRunning.rawValue,
HKQuantityTypeIdentifier.sixMinuteWalkTestDistance.rawValue
But when I add
HKCharacteristicTypeIdentifier.bloodType.rawValue
HKCharacteristicTypeIdentifier.dateOfBirth.rawValue
The permission screen does not asks for DOB and Blood Type. See Image:
Configuration: Simulator iOS 15.4 and Xcode 13.3
Anyone knows that if we can access Personal Data of HealthKit or not. Please help me out.
This is happening because bloodType and dateOfBirth are of type HKCharacteristicType
when you call this, the compactMap operation will not include your types
private static var allHealthDataTypes: [HKSampleType] {
let typeIdentifiers: [String] = [
HKQuantityTypeIdentifier.stepCount.rawValue,
HKQuantityTypeIdentifier.distanceWalkingRunning.rawValue,
HKQuantityTypeIdentifier.sixMinuteWalkTestDistance.rawValue,
HKCharacteristicTypeIdentifier.bloodType.rawValue,
HKCharacteristicTypeIdentifier.dateOfBirth.rawValue
]
return typeIdentifiers.compactMap { getSampleType(for: $0) }
}
check getSampleType:
func getSampleType(for identifier: String) -> HKSampleType? {
if let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier(rawValue: identifier)) {
return quantityType
}
if let categoryType = HKCategoryType.categoryType(forIdentifier: HKCategoryTypeIdentifier(rawValue: identifier)) {
return categoryType
}
return nil
}
your types won't fall into any of these if let, so this function will return nil. You must change the code so you are able to use HKCharacteristicTypeIdentifier as well.
EDIT: An easy way to do this is changing the readDataTypes in HealthData class to:
static var readDataTypes: [HKObjectType] {
return allHealthDataTypes + [
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.characteristicType(forIdentifier: .bloodType)!
]
}
You can only request read authorization for the HKCharacteristicTypes, not share authorization. Update your code to add these 2 data types only to the readDataTypes variable. Right now you are requesting both read & share for the characteristic types, which is why they are not appearing on the authorization sheet.
First, it's important to know that, OSLogStore didn't work in iOS as recently as 4 months ago. Since that's so recent and documentation is so sparse, what may have been true a year ago may not be true today.
Here's some context to my question (pretty much stolen from this reddit post):
I have about a 1000 users for an open source app I developed and every now and then users will report some odd behavior.
The app uses [Logger] to log important events ... Is there a remote logging service that can be used so that I can get this info and resolve issues for users?
I only care if the unified logging system has a solution to this problem. Let's assume I'm able to converse with the user with the "odd behavior" and they are non-technical.
I've been given some hints that OSLogStore may be a way to get remote logs, but the official documentation is so sparse, I can't tell. Specifically, that init(url:) seems interesting, but maybe it only accepts file:// protocols or something.
The logging documentation says it can be used "When you are unable to attach a debugger to the app, such as when you’re diagnosing problems on a user’s machine," but nowhere does it say how to do this.
After reading the discussion in this post, I wanted to make a simple prototype to see whether it is possible to get the logs from the phone remotely. To accomplish this, I modified Steipete's code a little: I removed some code I didn't need and added a button to trigger the sending of the logs, named "Send logs to the developers".
Then, I created a codable struct called SendableLog that converted the OSLogEntryLog, making it possible to convert it to JSON. After getting the logs using getEntries() and mapping them to this new type, I converted the logs to JSON and sent an HTTP POST request to an endpoint (as suggested by #DanielKaplan) on a simple Python server I was running on my MacBook.
The Swift code (iOS 15 application):
//
// ContentView.swift
// OSLogStoreTesting
//
// Created by bbruns on 23/12/2021.
// Based on Peter Steinberger (23.08.20): https://github.com/steipete/OSLogTest/blob/master/LoggingTest/ContentView.swift.
//
import SwiftUI
import OSLog
import Combine
let subsystem = "com.bbruns.OSLogStoreTesting"
func getLogEntries() throws -> [OSLogEntryLog] {
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
let allEntries = try logStore.getEntries(at: oneHourAgo)
return allEntries
.compactMap { $0 as? OSLogEntryLog }
.filter { $0.subsystem == subsystem }
}
struct SendableLog: Codable {
let level: Int
let date, subsystem, category, composedMessage: String
}
func sendLogs() {
let logs = try! getLogEntries()
let sendLogs: [SendableLog] = logs.map({ SendableLog(level: $0.level.rawValue,
date: "\($0.date)",
subsystem: $0.subsystem,
category: $0.category,
composedMessage: $0.composedMessage) })
// Convert object to JSON
let jsonData = try? JSONEncoder().encode(sendLogs)
// Send to my API
let url = URL(string: "http://x.x.x.x:8000")! // IP address and port of Python server
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
}
}
task.resume()
}
struct ContentView: View {
let logger = Logger(subsystem: subsystem, category: "main")
var logLevels = ["Default", "Info", "Debug", "Error", "Fault"]
#State private var selectedLogLevel = 0
init() {
logger.log("SwiftUI is initializing the main ContentView")
}
var body: some View {
return VStack {
Text("This is a sample project to test the new logging features of iOS 15.")
.padding()
Picker(selection: $selectedLogLevel, label: Text("Choose Log Level")) {
ForEach(0 ..< logLevels.count) {
Text(self.logLevels[$0])
}
}.frame(width: 400, height: 150, alignment: .center)
Button(action: {
switch(selectedLogLevel) {
case 0:
logger.log("Default log message")
case 1:
logger.info("Info log message")
case 2:
logger.debug("Debug log message")
case 3:
logger.error("Error log message")
default: // 4
logger.fault("Fault log message")
}
}) {
Text("Log with Log Level \(logLevels[selectedLogLevel])")
}.padding()
Button(action: sendLogs) {
Text("Send logs to developers")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have this simple Python HTTP server listening to incoming POST requests, the IP address was set to the local IP address of my MacBook. This matches the IP address in the Swift code above.
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
hostName = "x.x.x.x" # IP address of server
serverPort = 8000
class MyServer(BaseHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_HEAD(self):
self._set_headers()
def do_POST(self):
self._set_headers()
print("Received POST")
self.data_string = self.rfile.read(int(self.headers['Content-Length']))
self.send_response(200)
self.end_headers()
data = json.loads(self.data_string)
print(f"JSON received: \n\n {data}")
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print("Server started http://%s:%s" % (hostName, serverPort))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
When I run the app and tap the Send logs to developers button, I see the following message in my terminal:
x.x.x.x - - [23/Dec/2021 13:56:47] "POST / HTTP/1.1" 200 -
JSON received:
[{'subsystem': 'com.bbruns.OSLogStoreTesting', 'level': 3, 'composedMessage': 'SwiftUI is initializing the main ContentView', 'category': 'main', 'date': '2021-12-23 12:56:43 +0000'}]
The logs are successfully retrieved from the phone and then sent to the server.
Caveat
When I (fully) close the app and reopen it, the previous logs are gone!
When creating the log store (let logStore = try OSLogStore(scope: .currentProcessIdentifier)) the scope is set to .currentProcessIdentifier, which is the only available scope on iOS. This thread makes me believe The .system scope would include previous logs as well, but the system scope is not available on iOS.
re: #RobNapier's comment on the original post that says, “The only question is whether you can get logs off of remote devices ... and even that is pretty tricky.” I'm starting to think OSLogStore only gets local logs, but this enables you to send them anywhere, or do anything you want with them, really.
Now that OSLogStore works on iOS, you can put a button in your app labeled "Send logs to dev," where clicking it sends the logs to a custom endpoint on your server. That requires two steps:
Get the local logs.
Another part of the article you linked says:
With OSLogStore, Apple added an API to access the log archive programmatically. It allows accessing OSLogEntryLog, which contains all the log information you’ll possibly ever need. ... Let’s look at how this works:
func getLogEntries() throws -> [OSLogEntryLog] {
let subsystem = Bundle.main.bundleIdentifier!
// Open the log store.
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
// Get all the logs from the last hour.
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
// Fetch log objects.
let allEntries = try logStore.getEntries(at: oneHourAgo)
// Filter the log to be relevant for our specific subsystem
// and remove other elements (signposts, etc).
return allEntries
.compactMap { $0 as? OSLogEntryLog }
.filter { $0.subsystem == subsystem }
}
Send them to a custom endpoint on your server. With that function in your code base, I think you can use it like this:
let logs = getLogEntries();
sendLogsToServer(deviceId, appVersion, ..., logs); // this is your implementation
The one part that gives me pause is #RobNapier said getting "logs off of remote devices ... is pretty tricky." That makes me think there is something I'm missing. Hopefully #RobNapier will point out the flaws in my thinking.
App Crashing with error:
FIRESTORE INTERNAL ASSERTION FAILED: Invalid document reference. Document references must have an even number of segments, but users_table has 1
I am trying to store like this:-
func updateFirestorePushTokenIfNeeded() {
if let token = Messaging.messaging().fcmToken {
let usersRef = Firestore.firestore().collection("users_table").document(userID)
usersRef.setData(["fcmToken": token], merge: true)
}
}
My firestore is empty right now.
That error message almost certainly means that userID is nil or empty. You should verify that you're passing the correct values to Firestore.
I'm wondering if a user can read/write a row in the Parse Installation class that does not belong to their device?
I tried PFInstallation.query() and PFQuery(className: "Installation") to query the installation class to then edit and save the objects. The first method returned the following error: Clients aren't allowed to perform the find operation on the installation collection. The second returned no objects in the query.
Is it impossible to write to an Installation class row other than the PFInstallation.current() of the device? Or does someone know a proper method of how to do so?
Also, I am trying to do this from a client app, WITHOUT the Master Key
First Method:
let query = PFInstallation.query()
query?.findObjectsInBackground(block: { (rows, error) -> Void in
if error == nil {
print(rows)
} else {
print("Error: ", error!)
}
})
Second Method:
let inst = PFQuery(className: "Installation")
inst.whereKey("deviceType", equalTo: "ios")
inst.findObjectsInBackground(block: { (rows, error) -> Void in
if error == nil {
for row in rows! {
print("row: ", row)
}
} else {
print("ERROR: ", error!)
}
})
The class name is _Installation (All reserved table/class names start with an underscore _)
Allowed operations in _Installation (More here):
Get -> ignores CLP, but not ACL
Find -> master key only *
Create -> ignores CLP
Update -> ignores CLP, but not ACL **
Delete -> master key only **
Add Field -> normal behavior
* Get requests on installations follow ACLs normally. Find requests without master key is not allowed unless you supply the installationId as a constraint.
** Update requests on installations do adhere to the ACL defined on the installation, but Delete requests are master-key-only. For more information about how installations work, check out the installations section of the REST guide.
Extra:
It's both safer and simpler to use the class name provided by the class itself rather than entering the string value.
// Unsafe, can lead to typos, etc.
let query = PFQuery(className: "Installation")
// Better
let query = PFQuery(className: PFInstallation.parseClassName)
// Simpler
let query = PFInstallation.query()
I am trying to store a simple object (a custom class that has two String properties) in a Cloudant DB from an iOS App. I am making use of the Bluemix iOS SDK, specifically "Data for iOS 8". So far I am trying to apply the samples for the Swift programming language from the IBM Bluemix documentation.
The code to store an object is from the tutorials and has been only slightly adapted, if at all. The code below is contained in a function that is called when a text field's edit is done.
var remoteStore:CDTStore!
// initialize an instance of the IMFDataManager
let manager:IMFDataManager = IMFDataManager.sharedInstance()
let name = "pricingdb"
var testData = TestData(firstName: "aaaa", lastName: "bbbbb")
// Create remote store
manager.remoteStore(name, completionHandler: { (createdStore:CDTStore!, error:NSError!) -> Void in
if nil != error {
//Handle error
} else {
remoteStore = createdStore
println("Successfully created store: \(remoteStore.name)")
// Register class with remote store
remoteStore.mapper.setDataType("TestData", forClassName: NSStringFromClass(TestData.classForCoder()))
// Actually save the object to Bluemix
remoteStore.save(testData, completionHandler: { (savedObject:AnyObject!, error:NSError!) -> Void in
if nil != error {
//Save was not successful, handler received an error
println("Error: \(error)")
} else {
// Use the result
println("Saved revision: \(savedObject)")
}
})
}
})
The Cloudant database I want to store to exists. I can connect to the Datastore and the object is as well saved, the console output is:
HTTP_Status: 201
JSON Body: {
id = 6dfde55449915faa92c471bd0ecd89d6;
ok = 1;
rev = "1-419dfa2a14026c4c18545723e8f990fa";
}
Saved revision: <pricingtester.TestData: 0x7f91f1c6e2f0>
However, when looking at the JSON Body above I would expect to see the object data in there and, even if it wouldn't/shouldn't show there, I would expect to see it in the database entry that was created. But the corresponding database entry when checking the Cloudant dashboard is as follows:
{
"_id": "6dfde55449915faa92c471bd0ecd89d6",
"_rev": "1-419dfa2a14026c4c18545723e8f990fa",
"#datatype": "TestData"
}
What could I be missing?
A related question came up:
In the code above, the store is always created. If it exists, it is not recreated, but a reference to that store is made. If I know the store exists, which it does, how can I obtain a reference to that store without trying to first create it?
Can you provide the source code for the TestData class? For swift classes, String must currently use the NSString (ObjC) instead of the Swift String (e.g. var make:NSString)