I have been trying to upload an image to aws s3 server using shared module KMM. It works very well in Android but in iOS I have been getting this issue :- Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared
Now as much as I have searched about this I got to know it is related to something with frozen() but I am not sure what it is and how can I resolve this.
Code :-
actual class ClassName {
init {
ensureNeverFrozen()
}
actual fun imageUpload() {
var credentialsProvider = AWSCognitoCredentialsProvider(regionType = // Region here, identityPoolId = //identityPoolId here)
var configuration = AWSServiceConfiguration(region = // Region here, credentialsProvider = //credentialsProvider here)
AWSServiceManager.defaultServiceManager()?.defaultServiceConfiguration = configuration
val expression = AWSS3TransferUtilityUploadExpression()
// Start uploading using AWSS3TransferUtility
val awsTransferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
val completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock
completionHandler = { _: AWSS3TransferUtilityUploadTask?, error: NSError? ->
if (error == nil) {
val url = AWSS3.defaultS3().configuration.endpoint()?.URL()
val publicURL = url?.URLByAppendingPathComponent("bucketName")?.URLByAppendingPathComponent("fileName")
// Image Upload Complete
} else {
// Image Upload failure
}
}
awsTransferUtility.uploadFile(
fileUrl!!,
bucket = "bucketName",
key = "fileName",
contentType = ".image",
expression = expression,
completionHandler = completionHandler. // Error pointed on this line
)
}
}
Now as soon as I call the function my app gets crashed pointing error to the completionHandler.
Error log :-
Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared ClassName.$imageUpload$lambda-1$lambda-0$FUNCTION_REFERENCE$1#2803dc8 from other thread
at 0 iosApp 0x000000010cc1984f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95
at 1 iosApp 0x000000010cc138cd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93
at 2 iosApp 0x000000010cc139fd kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93
at 3 iosApp 0x000000010cc327fd kfun:kotlin.native.IncorrectDereferenceException#<init>(kotlin.String){} + 93
at 4 iosApp 0x000000010cc3461f ThrowIllegalObjectSharingException + 623
at 5 iosApp 0x000000010cd16fc2 _ZN12_GLOBAL__N_128throwIllegalSharingExceptionEP9ObjHeader + 34
at 6 iosApp 0x000000010cd170fd _ZN12_GLOBAL__N_136terminateWithIllegalSharingExceptionEP9ObjHeader + 13
at 7 iosApp 0x000000010cd1af0a _ZNK16KRefSharedHolder3refIL11ErrorPolicy3EEEP9ObjHeaderv + 58
at 8 iosApp 0x000000010cbf53ca _ZL39Kotlin_Interop_unwrapKotlinObjectHolderP11objc_object + 42
at 9 iosApp 0x000000010cbee050 _4b4d4d4c69623a736861726564_knbridge15 + 224
at 10 AWSS3 0x000000010d84509e -[AWSS3TransferUtility URLSession:task:didCompleteWithError:] + 4814
A native concurrency model available for preview. Check out New memory model migration guide. After release you shouldn't face any such problems, but until then the above answer is valid.
Try to call completionHandler.freeze()
Alternatively, move handler to function call(without storing it in a variable).
If inside the handler you're using some variables from outer scope, they may need to be frozen too. If none of first two methods works, try replacing content of the completion with just print() to see if it helps, and if it does - localize the problematic line by uncommenting parts of code one by one.
KMM concurrency model forbids accessing to mutable object from different threads, and freeze makes object non mutable so it can be used from different threads.
With coroutines objects gets frozen automatically when needed, but when you're switch threads without coroutines, you have to do it by your hands.
That's exactly what's happening here: AWS calls completionHandler from an other thread(which is quite usual for methods with completion)
Check out more about concurrency model here: https://kotlinlang.org/docs/mobile/concurrency-overview.html
This behaviour is what we had to manage with KMM for now, but soon it will be changed, this is the main blocker KMM to go from alpha to release, and the JetBrains team is focused on solving this particular problem so we don't have to use freeze() anymore.
Add below to gradle.properties
kotlin.native.binary.memoryModel=experimental
kotlin.native.binary.freezing=disabled
Related
According to GitHub sample project and Tealium's documentation for Kotlin I created such TealiumHelper:
object TealiumHelper {
fun init(application: Application) {
val tealiumConfig = TealiumConfig(
application,
accountName = BuildConfig.TEALIUM_ACCOUNT_NAME,
profileName = BuildConfig.TEALIUM_PROFILE_NAME,
environment = BuildConfig.TEALIUM_ENVIRONMENT
)
// Display logs only for DEV
Logger.Companion.logLevel = BuildConfig.TEALIUM_LOGLEVEL
// Make it start working
Tealium.create(BuildConfig.TEALIUM_INSTANCE, tealiumConfig)
}
fun trackEvent(name: String, data: Map<String, Any>? = null) {
val eventDispatch = TealiumEvent(name, data)
Tealium[BuildConfig.TEALIUM_INSTANCE]?.track(eventDispatch)
}
fun trackView(name: String, data: Map<String, Any>? = null) {
val viewDispatch = TealiumView(name, data)
Tealium[BuildConfig.TEALIUM_INSTANCE]?.track(viewDispatch)
}
}
I get logs by Tealium so it should be working fine.
2021-05-17 14:28:56.694 22812-22894/xxx.xxx.xxx D/Tealium-1.2.2: Dispatch(fc5c0) - Ready - {tealium_event_type=view, tealium_event=XXX ...}
But after I call trackView or trackEvent, my events don't go to server.
There is also additional log infor which I don't know what does it mean. Documentation doesn't say much about it:
2021-05-17 14:28:59.352 22812-22894/xxx.xxx.xxx I/Tealium-1.2.2: Asset not found (tealium-settings.json)
How could I fix it? What does Asset not found mean?
#deadfish I manage the mobile team here at Tealium, so I can point you in the right direction. You can safely ignore the Asset Not Found log - this just indicates that no local settings file was found, so by default, the remote settings will be used. We'll look at addressing this to make the log message a little more helpful. There are a couple of things that might cause data not to be sent. Firstly, I can't see any Dispatchers specified in your TealiumConfig object. As specified in the docs, you need to add either the TagManagement or Collect dispatcher, which are the modules that send the data from the device. The Collect module sends data to the server-side platform (I think this is what you need), while the TagManagement module processes the data client-side using JavaScript. Please feel free to get in touch with our support team or contact us by raising an issue on our GitHub repo if you need further personalized support.
I have this feature file :
Given I launch google using chrome as browser
When I search by kotlin
Then I will see 32 results per page
where my properties file has the content :
e1=www.google.com
field= kotlin
nr=32
However, the third steps from my feature file appears as if they aren't implemented : https://www.screencast.com/t/fP9vjdToSI
I have implemented the steps in a kotlin class :
init {
Given("^I launch \"(.*)\" using chrome as browser$") { element: String ->
// load the properties file
// prop.load(file)
prop.load(StepsDefinition::class.java!!.getClassLoader()
.getResourceAsStream("application-test.properties"))
// set the properties
prop.getProperty("el1")
val element= prop.getProperty("el1")
println(prop.getProperty("el1"))
driver.get(element)
When("^I search by \"(.*)\"$") { field: String ->
val button = driver.findElement(By.xpath("//input[#value=\"" + field+ "\"]" +
"| //button[contains(text(),\"" + field+ "\")]"))
button.isDisplayed
button.click()
}
Then("^I see \"(.*)\" results per page") { nr: String ->
val wait = WebDriverWait(driver, 10)
Assert.assertTrue(driver.getTitle().contains(nr))
}
Can someone please help me?
Thanks.
I don't have cucumber currently set up on my machine so I can't really verify what I'm saying, however I'd say it's just a mismatch between the step definition and the step in the feature.
Your step definition expects the keyword to be wrapped in quotes, while in your feature file you're not using any quotes. The same should apply to the When step (same error) and to the Then step, where you put an extra will.
I am trying to make an android app connecting to api, and for that I am using Kotlin Coroutines and Retrofit. I was following this tutorial (https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777) trying to setup my own api, but I stumbled on a problem. I can't get any data from api because I can't process response.
I don't know much about coroutines, so I don't understand whats the problem here. If I run debug and go line by line it is working perfectly each time, but if I run the app, it only prints TestPoint1. Also it doesn't throw any error and response is always 200 OK. I have tried to combine
val standings = service.getStandings()
and
val response = standings.await()
into one line, after which it doesn't work on debug either. It continues on code after launched coroutine.
val service = ApiFactory.footballApi
GlobalScope.launch(Dispatchers.Main) {
val standings = service.getStandings()
try {
Log.d("TAG", "TestPoint1")
val response = standings.await()
Log.d("TAG", "TestPoint2")
if(response.isSuccessful){
//store data
}else{
Log.d("MainActivity ",response.errorBody().toString())
}
}catch (e: Exception){
Log.d("TAG", "Error")
}
}
Switch the Dispatchers.Main to Dispatchers.IO . You can't make that request on the main thread .
The coroutines require a coroutine context in order to know in what thread they are going to run . For that , the Dispatchers class offer you some options . Currently you are making the request in the Dispatchers.Main which you cannot do because fetching data from the API , requires another thread . IO is the right thread for network calls .
Note :
Also please check : Internet permission, Internet connection .
I'm trying to generate keypairs in a React Native project. The key pair generation tool relies on the crypto module's random byte generation, which produces a buffer of a specified lengths with random byte values.
In order to use the crypto module within React Native, it has to be browserified, and the browserified random number generator looks like this:
https://github.com/crypto-browserify/randombytes/blob/master/browser.js
Here's the key component:
var crypto = global.crypto || global.msCrypto
if (crypto && crypto.getRandomValues) {
module.exports = randomBytes
} else {
module.exports = oldBrowser
}
Indeed, when debugging the application with Chrome, everything works fine, but when running it on iOS's JavaScriptCore engine, the oldBrowser method gets called instead, throwing the following error:
secure random number generation not supported by this browser
use chrome, FireFox or Internet Explorer 11
Thus I'm trying to find a replacement for the random bytes generation. One module I found is this one:
https://www.npmjs.com/package/react-native-randombytes
It uses the device's native libraries to generate a random number, and exposes it to React Native through their Obj-C/JS interface. It should be noted that this method only works on iOS, and the library's author doesn't have an Android solution yet, but that's an issue for another time.
This method works, in that it can generate random bytes, but it has one major drawback. React only supports asynchronous interfacing between Objective-C and JavaScript, which means that this method returns its results asynchronously. The original randomBytes method is synchronous, and pretty much every SDK out there that relies on it uses it synchronously. So if we were to go with the async version, all the SDKs would have to be rewritten for it, including all dependencies that rely on methods that used to be synchronous and now would no longer be.
Thus I'm trying to find a way to make the asynchronous native random number generator work synchronously. There are several node packages that do that, the most prominent one of them being deasync, but deasync relies on some core Node modules that cannot be browserified, so the synchronous version doesn't work.
Alternatively, I've tried wrapping it in a method that would set a semaphore, call the async generator, and wait in a while loop for the semaphore's value to change. That attempt failed because the while loop was blocking the callback from ever executing. Here's an approximation of my attempts, where the call to the async method has been replaced with a setTimeout, and the random number to be returned is a four, as determined by a fair dice roll.
function testSynchronicity() {
var isDone = false;
setTimeout(function() {
isDone = true;
}, 1000); // set isDone to true after a second
while (!isDone) {
// do nothing
}
return 4;
};
As this wasn't working, I figured I would try a completely different random number generator entirely, without the native-code-relying react-native-randombytes module and went with this one for JavaScript:
https://github.com/skeeto/rng-js
It worked fine within Node itself, but after browserifying it and trying to run the first example within React Native, it threw an error saying that the main object was not a constructor. Here's what the example looks like:
var RNG = require('./rng_react'); // rng_react is rng-js browserified
var rng = new RNG();
var randomValue = rng.random(0, 255, false);
So at this point, I'm at a bit of a loss, and would appreciate any help. Thanks!
EDIT: If all else fails, there's this, but I think it would pretty much beat the purpose of the question. https://github.com/bitpay/bitcore-lib/blob/master/lib/crypto/random.js#L37
I have found an answer that usually works. However, it is imperfect, because it works only if the randomBytes method is not required during app launch.
My solution does involve using the react-native-randombytes library. It relies on iOS's built-in CSPRNG to generate a random buffer, and then returns it asynchronously. In order to support synchronous responses, I expanded the moduel's randomBytes to not throw an error when no callback method is provided, but rather to use Stanford's JavaScript Crypto Library to generate random "words," as they're called, convert those to a buffer and then trim it accordingly:
var sjcl = require('sjcl');
var sjclRandom = new sjcl.prng(10);
var RNRandomBytes = require('react-native').NativeModules.RNRandomBytes;
module.exports.randomBytes = function(length, cb) {
if (!cb) {
var size = length;
var wordCount = Math.ceil(size * 0.25);
var randomBytes = sjclRandom.randomWords(wordCount, 10);
var hexString = sjcl.codec.hex.fromBits(randomBytes);
hexString = hexString.substr(0, size * 2);
return new Buffer(hexString, 'hex');
}
RNRandomBytes.randomBytes(length, function(err, base64String) {
if (err) {
cb(err);
} else {
cb(null, new Buffer(base64String, 'base64'));
}
});
};
The crux is, in order for the SJCL library to have sufficient entropy, it needs to have been seeded properly. So, on startup, we use the asynchronous CSPRNG functionality to seed the SJCL random number generator:
module.exports.randomBytes(4096, function(err, buffer) {
var hexString = buffer.toString('hex');
// we need to convert the hex string to bytes, or else SJCL assumes low entropy
var stanfordSeed = sjcl.codec.hex.toBits(hexString);
sjclRandom.addEntropy(stanfordSeed, 10, 'csprng');
});
Thus, we have a synchronous randomBytes method within React Native, provided we have had the opportunity to call it asynchronously at least once before we need its synchronous functionality.
Your solution does answer the question but seems a bit complex. In particular, why not use only SJCL?
In my case I've ended up using react-native-securerandom, which is just a thin wrapper over Android and iOS native calls. Then I've done this to initialise SJCL's RNG:
const { generateSecureRandom } = require('react-native-securerandom');
const sjcl = require('lib/vendor/sjcl');
const randomBytes = await generateSecureRandom(1024/8);
let temp = [];
for (let n in randomBytes) {
if (!randomBytes.hasOwnProperty(n)) continue;
temp.push(randomBytes[n].toString(16));
}
const hexSeed = sjcl.codec.hex.toBits(temp.join(''));
sjcl.random.addEntropy(hexSeed, 1024, 'generateSecureRandom');
We build an android application with Scala 2.11. We use scala.concurrent.Future for async background tasks. The problem is, that we do not see any exceptions in logcat if exceptions are thrown inside a Future block.
We already create an execution context with our own reporter:
lazy val reporter: (Throwable => Unit) = {
t =>
t.printStackTrace()
}
implicit lazy val exec = ExecutionContext.fromExecutor(
new ThreadPoolExecutor(10, 100, 5, TimeUnit.MINUTES,
new LinkedBlockingQueue[Runnable]), reporter)
Even if I set a breakpoint inside the reporter the debugger does never stop here, even if I force throwing of exceptions insde a Future {...} block.
What are we doing wrong
According to your comment it looks like you simply didn't work with Future as needed. When some exception occurred during the Future computation it is transformed into Failure case (like Failure from Try, but in async context), e.g:
scala> Future { 10 / 0 }
res21: scala.concurrent.Future[Int] = scala.concurrent.impl.Promise$DefaultPromise#24f3ffd
As you can see there is no exception thrown or printed. To handle this exception you need to use callbacks, i.e onComplete or onFailure, e.g:
scala> res21.onFailure { case error => println(s"Error: ${ error.getMessage }") }
Error: / by zero
A great intro into Futures and Duality was given by the man with psychedelic T-Shirt Erik Meijer in the coursers intro to Reactive Programming.