Im trying to connect my app to a Realm Object Server. The documentation says, to do this you use the below:
// create a configuration object
let realmUrl = URL(string: "realms://example.com:9000/~/settings")!
let realmUser = SyncCredentials.usernamePassword(username: username, password: password)
let config = Realm.Configuration(user: realmUser, realmURL: realmUrl)
// open the Realm with the configuration object
let settingsRealm = try! Realm(configuration: config)
However for SyncCredentials.usernamePassword, XCode says SyncCredentials doesn't exist. From the looks you need to set SyncConfiguration on in Realm.configuration (or the only file I can find RealmConfiguration.swift)
Now I'm in that file theres no option to use SyncCredentials
My question is, how do I simply connect my app with a Realm Object Database using SyncCredentials (or however you're supposed to do it). Been pulling my hair out all day over this, surely it can't be that hard :-(
There seem some mistakes.
SyncCredentials is auth info that is used to log in. It is not user object. Realm.Configuration doesn't receive user and realmURL parameters in the initializer. You need to use SyncConfiguration instead.
The example code for logging in or instantiating Realm with existing user is the following.
let syncServerURL = URL(string: "realm://example.com:9080/~/settings")!
let syncAuthURL = URL(string: "http://example.com:9080")!
if let user = SyncUser.current {
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: syncServerURL))
let realm = try! Realm(configuration: config)
...
} else {
let username = ...
let password = ...
let credentials = SyncCredentials.usernamePassword(username: username, password: password)
SyncUser.logIn(with: credentials, server: syncAuthURL) { user, error in
DispatchQueue.main.async {
if let user = user {
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user, realmURL: syncServerURL))
let realm = try! Realm(configuration: config)
...
} else {
// Handle error
}
}
}
}
Please read our documentation again https://realm.io/docs/swift/latest/#sync
and also our RealmTasks sample project helps you to understand interacting Realm Object Server. https://github.com/realm-demos/realm-tasks
If you still see SyncCredentials not found error in above code, probably you didn't setup RealmSwift framework correctly. Please add more info that how did you set up Realm.
Swift Package Manager doesn't load SyncCredentials or SyncUser properly.
I was using the SPM to install RealmSwift as a dependency from IceCream. I removed Realm and IceCream from SPM. Then installed using Carthage to solve the issue.
Related
is there a good function to download images from AWS S3 bucket? I have an access key and a secret key for permisson. The URL is thru a different database accessible. I also already imported AWSS3 and AWSCore.
I have already found a upload function:
func uploadFile(withImage image: UIImage) {
let access = "access_key"
let secret = "secret_key"
let credentials = AWSStaticCredentialsProvider(accessKey: access, secretKey: secret)
let configuration = AWSServiceConfiguration(region: AWSRegionType.EUCentral1, credentialsProvider: credentials)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let s3BucketName = "bucket_name"
let compressedImage = image.resizedImage(newSize: CGSize(width: 80, height: 80))
let data: Data = compressedImage.pngData()!
let remoteName = generateRandomStringWithLength(length: 12)+"."+data.format
print("REMOTE NAME : ",remoteName)
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = { (task, progress) in
DispatchQueue.main.async(execute: {
// Update a progress bar
})
}
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (task, error) -> Void in
DispatchQueue.main.async(execute: {
// Do something e.g. Alert a user for transfer completion.
// On failed uploads, `error` contains the error object.
})
}
let transferUtility = AWSS3TransferUtility.default()
transferUtility.uploadData(data, bucket: s3BucketName, key: remoteName, contentType: "image/"+data.format, expression: expression, completionHandler: completionHandler).continueWith { (task) -> Any? in
if let error = task.error {
print("Error : \(error.localizedDescription)")
}
if task.result != nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(s3BucketName).appendingPathComponent(remoteName)
if let absoluteString = publicURL?.absoluteString {
// Set image with URL
print("Image URL : ",absoluteString)
}
}
return nil
}
}
I would not recommend to download files directly from S3 using an access and secret key.
I'd propose you do the following:
Make sure the bucket is as "private" as can be.
Have an API with authentication and authorisation (AWS API Gateway) that checks if the user is authenticated and permitted to download the S3 object.
Generate a pre-signed download URL with that is only valid for a short period of time (15-60 minutes).
Return that pre-signed download URL to your app through the API.
Use the URL within your app to download the S3 object.
This way you don't have to ship username and password in your app and the bucket is closed off to the "outside" reducing the risk of accidental information leakage.
Why I wouldn't recommend using the access key and secret key:
This is a potential security issue. People that reverse engineer the app could gain access to those "static" keys and depending on the underlying IAM role do all sorts of harm. But even if you have proper IAM roles with very limited access, essentially shipping a username and password with your app is not a good idea under any circumstance. How would you "rotate" the secret if something bad happens etc.
I have an app that authenticates with Cognito and has been working fine.
Now I need to reuse the authentication and returned token to upload files to S3. My understanding is that AWSS3TransferUtility is the way to go at this point. Only it is not clear what needs to be done and how is the token to be passed to S3? Can anyone point to an example? Just using examples available suggesting to do:
let credentialsProvider =
CredentialsProvider(regionType:region, identityPoolId:poolId)
let serviceS3Configuration = AWSServiceConfiguration(region:region, credentialsProvider: credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = serviceS3Configuration
results in "Unauthenticated access is not supported for this identity pool" assuming that this is because token is not being used and S3 AWS service is not initialized correctly. But I do not see a way to set it ? what am I missing? I can see some examples suggesting setting logins property of credentialsProvider to
AWSCognitoLoginProviderKey but seems to be outdated at this point.
Any and all help would be greatly appreciated.
S3 configuration with Cognito is a little mysterious. The answers are in the docs but not entirely obvious. The core of getting this to work is registering your AWSMobileClient instance with the configuration.
Simplified code without error checking:
Boot up your AWSMobileClient:
AWSMobileClient.sharedInstance().initialize({ { userstate, error in
if userstate != nil {
registerAuthentication(credentialsProvider: AWSMobileClient.sharedInstance())
}
})
Once completed pass the sharedInstance to AWSServiceConfiguration because AWSMobileClient is-a AWSCredentialsProvider
let DefaultTransferUtilityKey = "DEFAULT_AUTH_KEY"
func registerAuthentication(credentialsProvider: AWSCredentialsProvider) {
/// only do this once per app launch
/// assumes you're using the plist config method
guard let s3tranferInfo = AWSInfo.default().defaultServiceInfo("S3TransferUtility"),
let bucketName = s3tranferInfo.infoDictionary["Bucket"] as? String else {
assertionFailure("failed to load /S3TransferUtility/Bucket key - is awsconfiguration.json correct ?")
return
}
let transferConfig = AWSS3TransferUtilityConfiguration()
transferConfig.bucket = bucketName
if let serviceconfiguration = AWSServiceConfiguration(region: s3tranferInfo.region, credentialsProvider: credentialsProvider) {
AWSS3TransferUtility.register(with: serviceconfiguration, transferUtilityConfiguration: transferConfig, forKey: DefaultTransferUtilityKey)
}
}
and once that registration is actually finished you can access the transfer utility via the common key string.
lazy var transferUtility: AWSS3TransferUtility = {
let utility = AWSS3TransferUtility.s3TransferUtility(forKey: DefaultTransferUtilityKey)
return utility
}()
Bucket name and region could be strings also but if you're using AWSMobileClient you probably have the plist setup.
I am trying to connect to my AWS AppSync service from my Swift mobile app using the AWS Swift SDK but keep getting the following error:
Error occurred: (401 unauthorized) Did not receive a successful HTTP code.
I am using User Pools and have set everything up following the tutorial for swift. My question is, how do I incorporate the AppSync.json config file generated in the console in my request? That is not mentioned in the tutorial and may be the reason I cannot connect.
The json file looks like this:
{
"graphqlEndpoint": "my_endpoint_url",
"region": "us-east-1",
"authenticationType": "AMAZON_COGNITO_USER_POOLS",
"apiKey": null
}
At the moment I am using the following configuration:
// Set up Amazon Cognito credentials
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: CognitoIdentityRegion,
identityPoolId: CognitoIdentityPoolId, identityProviderManager: pool)
// You can choose your database location, accessible by SDK
let databaseURL = URL(fileURLWithPath:NSTemporaryDirectory()).appendingPathComponent(database_name)
do {
// Initialize the AWS AppSync configuration
let appSyncConfig = try AWSAppSyncClientConfiguration(url: AppSyncEndpointURL,
serviceRegion: AppSyncRegion,
credentialsProvider: credentialsProvider,
databaseURL:databaseURL)
appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
// Set id as the cache key for objects
appSyncClient?.apolloClient?.cacheKeyForObject = { $0["id"] }
} catch {
print("Error initializing appsync client. \(error)")
}
EDIT #1
It turns out the example is using the API key method rather than user pools. So now I have changed the config to:
let appSyncConfig = try AWSAppSyncClientConfiguration(url: AppSyncEndpointURL, serviceRegion: AppSyncRegion, userPoolsAuthProvider: CognitoUserPoolsAuthProvider(pool: pool))
The problem is the message now is:
Use of unresolved identifier 'CognitoUserPoolsAuthProvider'
if I try:
let appSyncConfig = try AWSAppSyncClientConfiguration(url: AppSyncEndpointURL, serviceRegion: AppSyncRegion, userPoolsAuthProvider: AWSCognitoUserPoolsAuthProvider(pool: pool))
the error is:
'AWSCognitoUserPoolsAuthProvider' cannot be constructed because it has no accessible initializers
Not sure how to satisfy the userPoolsAuthProvider: argument in the config.
To address your specific issue, userPoolsAuthProvider needs to accept a class that extends the AWSCognitoUserPoolsAuthProvider protocol. So your instantiation would look something like this:
let appSyncConfig = try AWSAppSyncClientConfiguration(url: AppSyncEndpointURL,
serviceRegion: AppSyncRegion,
userPoolsAuthProvider: self,
databaseURL:databaseURL)
And then in the class in which you are creating the AppSyncClient, you would extend like this:
extension YourClass: AWSCognitoUserPoolsAuthProvider {
func getLatestAuthToken() -> String {
var token: String = ""
// pool is an instance of AWSCognitoIdentityUserPool
self.pool?.currentUser()?.getSession().continueOnSuccessWith(block: { (task) -> Any? in
token = task.result!.idToken!.tokenString
return nil
}).waitUnitFinished()
}
return token
}
Also I think the AppSync configuration by default looks for an awsconfiguration.json file in your project. You posted this a while back so possibly things have changed with AWS services and AppSync.
Hope this helps
I'm attempting to save a twitter users username into the database for later reference my code below is executing but doesn't seem to be accessing the database or saving the username into the database and I'm really lost as to why. I'm attempting to have the username and userID so I can retrieve information about the user for a profile page in the app. So if I can avoid saving this data to the database all together that works too but I don't think it can be done that way.
fileprivate func setupTwitterButton() {
let twitterButton = TWTRLogInButton { (session, error) in
if let err = error {
print("Failed to login via Twitter: ", err)
return
}
// debug statement
//print("Successfully logged in using Twitter")
HUD.show(.labeledProgress(title: nil, subtitle: "Signing In"))
//we've authenticated twitter, time to log into firebase
guard let token = session?.authToken else { return }
guard let secret = session?.authTokenSecret else { return }
let creds = FIRTwitterAuthProvider.credential(withToken: token, secret: secret)
let dbref = FIRDatabase.database().reference()
let usersref = dbref.child("users")
let uid = session?.userID
//let user = FIRAuth.auth?.signIn
print("Creating user")
let newUserReference = usersref.child(uid!)
newUserReference.setValue(["username": session?.userName])
Okay so after some debugging it was pretty simple where I went wrong. I was trying to write to the database before I'd authenticated with the database. Once I had put my code for writing to the database after I'd authenticated it all worked correctly.
I have this code, which should append messages with a new message:
func addMessage(_ message: Message) {
do {
try Realm().write {
self.messages.append(message)
}
} catch let error {
print("could not add message due to error:\n\(error)")
}
}
However, I get an exception Cannot modify managed RLMArray outside of a write transaction It doesn't make any sense to me, because I'm already in a write transaction...
You need to create a Realm object before applying the write module.
According to the GitHub documentation, you can try code like this:
func addMessage(_ message: Message) {
do {
let realm = try! Realm()
try! realm.write {
self.messages.append(message)
}
} catch let error {
print("Could not add message due to error:\n\(error)")
}
}
Hope it helps!
You can use let realm = try! Realm() with a custom default configuration by setting a default configuration see here:
var config = Realm.Configuration()
// Set this as the configuration used for the default Realm
Realm.Configuration.defaultConfiguration = config
The trouble was I was using a plain Realm object with no special configuration. Since I am using Realm Mobile Platform, I needed to create a Realm object with the same config each time I want to write to that DB:
let configuration = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://127.0.0.1:9080/~/speciail")!)
)
self.realm = try! Realm(configuration: configuration)
//now do the write transaction!
It took a bit of refactoring, but I have it now. My thanks to those of you who took the time to help me.