DatabaseException after minifyEnabled true - exception

This entity working with Firebase RealmDataBase correcrly, but when i add
minifyEnabled true
in gradle for build, i got this ->
DatabaseException: Found two getters or fields with conflicting case sensitivity for property: sName
p.s. If I change the name of the fields that give me this exception, then these names will be saved on the server, I think the problem starts with annotations for the fields
Its my entity
#IgnoreExtraProperties
data class Account(
#get:PropertyName(ACCOUNT_NAME) #set:PropertyName(ACCOUNT_NAME) var name: String,
#get:PropertyName(ACCOUNT_SNAME) #set:PropertyName(ACCOUNT_SNAME) var sName: String,
#get:PropertyName(ACCOUNT_CITY) #set:PropertyName(ACCOUNT_CITY) var city: String,
#get:PropertyName(ACCOUNT_GYM) #set:PropertyName(ACCOUNT_GYM) var gym: String,
#get:PropertyName(ACCOUNT_WEIGHT) #set:PropertyName(ACCOUNT_WEIGHT) var weight: String,
#get:PropertyName(ACCOUNT_HEIGHT) #set:PropertyName(ACCOUNT_HEIGHT) var height: String,
#get:PropertyName(ACCOUNT_URI_PICTURE) #set:PropertyName(ACCOUNT_URI_PICTURE) var imageUrl: String,
#Exclude #set:Exclude #get:Exclude var followers: List<String> = emptyList(),
#Exclude #set:Exclude #get:Exclude var subscribers: List<String> = emptyList(),
#Exclude #set:Exclude #get:Exclude var countAchievements: List<String> = emptyList()
) : AccountListGrouper, Parcelable {
#Exclude
override fun getItemType() = TYPE_ACCOUNT
constructor(source: Parcel) : this(
source.readString() ?: "",
source.readString() ?: "",
source.readString() ?: "",
source.readString() ?: "",
source.readString() ?: "",
source.readString() ?: "",
source.readString() ?: ""
)
#Exclude
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(name)
dest.writeString(sName)
dest.writeString(city)
dest.writeString(gym)
dest.writeString(weight)
dest.writeString(height)
dest.writeString(imageUrl)
}
constructor() : this("", "", "", "", "", "", "")
#Exclude
override fun describeContents() = 0
companion object {
#JvmField
val CREATOR: Parcelable.Creator<Account> = object : Parcelable.Creator<Account> {
override fun createFromParcel(source: Parcel): Account = Account(source)
override fun newArray(size: Int): Array<Account?> = arrayOfNulls(size)
}
}
}
Its all that i try add to proguard-rules.pro
-keep class com.voitenko.dev.training_booster.** { *; }
-keep class com.sdsmdg.harjot.** { *; }
#Firebase
-keepattributes Signature
-keepclassmembers class com.voitenko.dev.training_booster.models** { *;}
-keepnames class com.voitenko.dev.training_booster.** { *; }
-keepattributes *Annotation*
-keep class com.google.firebase.** { *; }
#_____________
# Needed for DNS resolution. Present in OpenJDK, but not Android
-dontwarn javax.naming.**
# Don't warn about checkerframework
#
# Guava uses the checkerframework and the annotations
# can safely be ignored at runtime.
-dontwarn org.checkerframework.**
# Guava warnings:
-dontwarn java.lang.ClassValue
-dontwarn com.google.j2objc.annotations.Weak
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn javax.lang.model.element.Modifier
# Okhttp warnings.
-dontwarn okio.**
-dontwarn com.google.j2objc.annotations.**

The solution to this problem was add
#Exclude
for ALL! for all variables. I think this is necessary because during compilation of kotlin to Java, get () and set () are created for the public variable: In this case, Firebase does not know which option to use, either the public variable or the get and set methods
So my code now it
#IgnoreExtraProperties
data class Account(
#Exclude #get:PropertyName(ACCOUNT_NAME) #set:PropertyName(ACCOUNT_NAME) var name:
String = "",
#Exclude #get:PropertyName(ACCOUNT_SNAME) #set:PropertyName(ACCOUNT_SNAME) var sName:
String = "",
#Exclude #get:PropertyName(ACCOUNT_CITY) #set:PropertyName(ACCOUNT_CITY) var city:
String = "",
#Exclude #get:PropertyName(ACCOUNT_GYM) #set:PropertyName(ACCOUNT_GYM) var gym:
String = "",
#Exclude #get:PropertyName(ACCOUNT_WEIGHT) #set:PropertyName(ACCOUNT_WEIGHT) var
weight: String = "",
#Exclude #get:PropertyName(ACCOUNT_HEIGHT) #set:PropertyName(ACCOUNT_HEIGHT) var
height: String = "",
#Exclude #get:PropertyName(ACCOUNT_URI_PICTURE)
#set:PropertyName(ACCOUNT_URI_PICTURE) var imageUrl: String = "",
#Exclude #set:Exclude #get:Exclude var followers: List<String> = emptyList(),
#Exclude #set:Exclude #get:Exclude var subscribers: List<String> = emptyList(),
#Exclude #set:Exclude #get:Exclude var countAchievements: List<String> = emptyList()
)
But i dont know why its worked correctly without minifyEnabled true ^_^

Related

I tried to parse a JSON file on my app but even if there is no error, my app doesn't run on the emulator

The problem might be that the app can't reach the json file I tried to parse it to.No error pops up in the run or build but it stops at"11/23 23:14:38: Launching 'app' on Device 4.Install successfully finished in 325 ms."Below is my code:
MainActivity
class MainActivity : AppCompatActivity() {
#SuppressLint("SuspiciousIndentation", "NotifyDataSetChanged")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val usersList: ArrayList<UserModelClass> = ArrayList()
try {
val obj = JSONObject(getJSONFromAssets()!!)
val usersArray = obj.getJSONArray("owner")
for (i in 0 until usersArray.length()) {
val user = usersArray.getJSONObject(i)
val login = user.getInt("id")
val name = user.getString("name")
val url = user.getString("url")
val followers_url = user.getString("followers")
val starred_url = user.getString("stars")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}
val recyclerView = findViewById<RecyclerView>(R.id.rvUsersList)
val adapter = UserAdapter(this, usersList)
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
recyclerView.adapter = adapter
adapter.notifyDataSetChanged()
}
private fun getJSONFromAssets(): String? {
var json: String? = null
val charset: Charset = Charsets.UTF_8
try {
val myUsersJSONFile = assets.open("JSON.json")
val size = myUsersJSONFile.available()
val buffer = ByteArray(size)
myUsersJSONFile.read(buffer)
myUsersJSONFile.close()
json = String(buffer, charset)
} catch (ex: IOException) {
ex.printStackTrace()
return null
}
return json
}
}
UserAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
class UserAdapter( val context: Context, val items: ArrayList<UserModelClass>) :
RecyclerView.Adapter<UserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
return UserViewHolder(
LayoutInflater.from(context).inflate(
R.layout.item_user,
parent,
false
)
)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
}
UserViewHolder
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each item to
var tvId : TextView
var tvName : TextView
var tvURL : TextView
var tvFollowers : TextView
var tvStars : TextView
init {
tvId = view.findViewById(R.id.tvId)
tvName = view.findViewById(R.id.tvName)
tvURL = view.findViewById(R.id.tvURL)
tvFollowers = view.findViewById(R.id.tvFollowers)
tvStars = view.findViewById(R.id.tvStars)
}
fun bind(item: UserModelClass) {
tvId.text = item.login.toString()
tvName.text = item.node_id
tvURL.text = item.avatar_url
tvFollowers.text = item.followers_url.toString()
tvStars.text = item.starred_url.toString()
}
}
UserModelClass
class UserModelClass(
val login: Int,
val node_id: String,
val avatar_url: String,
val followers_url: String,
val starred_url: String
)
Json.json
I don't need all the information from the json, I selected some of them
{
"total_count": 357602,
"incomplete_results": false,
"items": [
{
"id": 23096959,
"node_id": "...",
"name": "...",
"full_name": "...",
"private": false,
"owner": {
"login": "...",
"id": 4314092,
"node_id": "...",
"avatar_url": "https://...",
"url": "https://api.github.com/users/golang",
"followers_url": "https://api.github.com/users/golang/followers",
"following_url": "https://api.github.com/users/golang/following{/other_user}",
"starred_url": "https://api.github.com/users/golang/starred{/owner}{/repo}",
},
Actually it seems you are parsing it wrong. In your json, items is the array and owner is an object inside items array item.
You cannot access owner directly. Also you should give exact keys.
Like "followers_url" not just "followers"
Try the below code.
try {
val obj = JSONObject(getJSONFromAssets()!!)
val itemsArray = obj.getJSONArray("items")
for (i in 0 until itemsArray.length()) {
val user = itemsArray.getJSONObject(i)
val name = user.getString("name") // Getting name from item object
val owner = user.getJSONObject("owner") //Getting owner object from item object
//And below you need to get items from owner object not the user object.
val login = owner.getInt("id")
val url = owner.getString("url")
val followers_url = owner.getString("followers_url")
val starred_url = owner.getString("starred_url")
val userDetails =
UserModelClass(login, name, url, followers_url , starred_url)
// add the details in the list
usersList.add(userDetails)
}
} catch (e: JSONException) {
//exception
e.printStackTrace()
}

Swift Tree Structure, encoding and accessing nodes

I am having trouble figuring out why the following does not run. The goal is to create a tree structure with reference types that can be saved to a json file, but the following playground code does not run.
The root node has a nil parent, but I thought the encoder ignored nil values. In my app I get an EXC_BAD_ACCESS. Does this need to be done with structs instead of classes, and if so, is there a way to access a particular node without traversing the entire tree? Any help appreciated.
import Cocoa
final class Node: Codable {
var id: UUID
var data: [MyData]
var children: [Node]
var parent: Node? = nil
init() {
self.id = UUID()
self.data = []
self.children = []
}
func add(data: MyData) {
data.parent = self
self.data.append(data)
}
func add(child: Node) {
child.parent = self
self.children.append(child)
}
}
final class MyData: Codable {
var id: UUID
var label: String
var value: String
var parent: Node? = nil
init(label: String, value: String) {
self.id = UUID()
self.label = label
self.value = value
}
}
var root = Node()
root.add(data: MyData(label: "label 1", value: "value 1"))
root.add(data: MyData(label: "label 2", value: "value 2"))
var child = Node()
child.add(data: MyData(label: "label 3", value: "value 3"))
root.add(child: child)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let json = try encoder.encode(root)
print(String(data: json, encoding: .utf8)!)
The problem is that by default Codable tries to encode/decode all properties of a conforming type. This means that the children try to encode/decode their parent, which parent contains the children as well, hence leading to an infinite loop.
You need to manually specify which properties to encode/decode by providing a CodingKey conformant type. By leaving out the parent property from both Node.CodingKeys and MyData.CodingKeys, you resolve the infinite loop.
import Foundation
final class Node: Codable {
let id = UUID()
var data: [MyData]
var children: [Node]
weak var parent: Node? = nil
init() {
self.data = []
self.children = []
}
func add(data: MyData) {
data.parent = self
self.data.append(data)
}
func add(child: Node) {
child.parent = self
self.children.append(child)
}
private enum CodingKeys: String, CodingKey {
case data
case children
}
}
final class MyData: Codable {
let id = UUID()
var label: String
var value: String
weak var parent: Node? = nil
init(label: String, value: String) {
self.label = label
self.value = value
}
private enum CodingKeys: String, CodingKey {
case label
case value
}
}
var root = Node()
root.add(data: MyData(label: "label 1", value: "value 1"))
root.add(data: MyData(label: "label 2", value: "value 2"))
var child = Node()
child.add(data: MyData(label: "label 3", value: "value 3"))
root.add(child: child)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let json = try encoder.encode(root)
print(String(data: json, encoding: .utf8)!)

Moshi error: #JsonClass can't be applied to [class]. must not be sealed

Imagine this data sample
"meta_data": [
{
"id": 40097,
"key": "_wcf_frm_created",
"value": ""
},
{
"id": 40098,
"key": "_wcf_custom_degin_checkbox",
"value": ""
},
{
"id": 40099,
"key": "_wcf_frm_data",
"value": {
"1": {
"1": "",
"2": "",
"3": "chk_box"
}
}
},
{
"id": 40119,
"key": "_vendor_select",
"value": "6484"
},
{
"id": 40120,
"key": "_vendor_percentage",
"value": "1"
},
{
"id": 40121,
"key": "_vendor_pro_cat",
"value": "Accessories"
}
]
the Value in Meta_data can have multiple types. In the generator I used shown that the data type should be created like this.
sealed class Value {
class StringMapMapValue(val value: Map<String, Map<String, String>>) : Value()
class StringValue(val value: String) : Value()
}
With Moshi, I understand you have to add #JsonClass(generateAdapter = true) on top of the data class. Thus I have something like this
#JsonClass(generateAdapter = true)
data class MetaDatum (
val id: Long,
val key: String,
val value: Value
)
#JsonClass(generateAdapter = true)
sealed class Value {
class StringMapMapValue(val value: Map<String, Map<String, String>>) : Value()
class StringValue(val value: String) : Value()
}
I would like to note that the full json is much bigger than this. However, this is the only issue I have. I had some Enum issues as well, but those can be replaced with String
The error I received is
error: #JsonClass can't be applied to net......Activity.Value: must not be sealed
public static abstract class Value
Thus my question is, how do i decode the json with multiple enum types.
Ill add this here, In xCode(swift) this was how i was manage to do it.
enum Value: Codable {
case string(String)
case stringMapMap([String: [String: String]])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode([String: [String: String]].self) {
self = .stringMapMap(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Value.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Value"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let x):
try container.encode(x)
case .stringMapMap(let x):
try container.encode(x)
}
}
}
Calling the data
fun retrieveMenu(sku: Int, SSLAuth: String)
{
doAsync {
val client = OkHttpClient().newBuilder()
.build()
val formBody: RequestBody = FormBody.Builder()
.build()
val request: Request = Request.Builder()
.url("https://carteapp.net/..................")
.method("Get", formBody)
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
val gist =
gistJsonAdapter.fromJson(response.body!!.source())
println(gist)
}
}
}
private val moshi = Moshi.Builder().build()
private val gistJsonAdapter = moshi.adapter(BarcodeScannerActivity.WcProductCall::class.java)
#JsonClass(generateAdapter = true)
data class WcProductCall (
val id: Long,
...........
val metaData: List<MetaDatum>,
...
)
This is a bit tricky as Moshi won't allow sealed/abstract classes, my idea would be to use the DTO pattern (This is a pattern for using a Data Transfer Object to pass as much data in a single network request as possible)
Create a data class (your DTO) that contains both / all the data
#JsonClass(generateAdapter = true)
data class MetaDatumDTO (
val id: Long,
val key: String,
val value1: Value1DTO?,
val value2: Value2DTO?
)
#JsonClass(generateAdapter = true)
data class Value1DTO(val value: Map<String, Map<String, String>>)
#JsonClass(generateAdapter = true)
data class Value2DTO(val value: String)
And then inside your repository / model, when you retrieve the data, use a mapper function to map the data to the data classes you want to use
data class MetaDatum(
val id: Long,
val key: String,
val value: Value?
)
sealed class Value {
data class Value1(val value: Map<String, Map<String, String>>) : Value()
data class Value2(val value: String) : Value()
}
fun MetaDatumDto.mapFromNetworkRequest(): MetaDatum {
return MetaDatum(
id = id,
key = key,
value = getValueFromDTO()
)
}
fun MetaDatumDto.getValueFromDTO(): Value? {
return (value1 as? Value1) ?: (value2 as? Value2)
}

Parsing JSON with Argo on Swift

I'm attempting to parse JSON data with next format:
["1", "string", "", "0.16"]
And these "weird" json should map in next way to my object:
myObject.id = json[0] //"1"
myObject.name = json[1] //"string"
myObject.surname = json[2] // ""
myObject.length = json[3] // "0.16"
I'm using Argo for parsing, there is example of my code
public struct SomeObject {
public var id: String
public var name: String
public var surname: String
public var length: Float
}
extension SomeObject: Decodable {
static func create(id: String)(name: String)(surname: String)(length: String) -> SomeObject {
return SomeObject(id: id, name: name, surname: surname, length: length)
}
public static func decode(json: JSON) -> Decoded<SomeObject> {
return SomeObject.create <- actually don't know what to put here, i tried json[0], and decode(json[0]) and casting but still no luck
}
What is the correct way to parse that kind of JSON data?
For your information:
let ar = ["1", "string", "", "0.16"]
public struct SomeObject {
public var id: String?
public var name: String?
public var surname: String?
public var length: Float?
}
extension SomeObject {
static func create(id: String?, name: String?, surname: String?, length: Float?) -> SomeObject {
return SomeObject(id: id, name: name, surname: surname, length: length)
}
public static func decode(json: AnyObject?) -> SomeObject {
let js = json as! Array<AnyObject>
return SomeObject.create(js[0] as? String, name: js[1] as? String, surname: js[2] as? String, length: js[3] as? Float)
}
}
let someObject = SomeObject.decode(ar)
Argo isn't setup to pull out certain indices from arrays like this. What you'll have to do is first decode it to a [String] then pick out the indices you want.
let values: Decoded<[String]> = decodeArray(json)
return SomeObject.create
<^> ({ $0[0] } <^> values)
<*> ({ $0[1] } <^> values)
<*> ({ $0[2] } <^> values)
<*> (values >>- { .fromOptional(Float($0[3])) })
You can see that I use closures to pull out the required index. The last one also casts the String to a Float to match your types.
Besides this parsing code, you could also improve the model by using let instead of var to make it immutable as well as use the Curry framework (https://github.com/thoughtbot/Curry) instead of creating your own curried initializer.

Realm.io and AFNetworking interaction

I have this request:
let manager = AFHTTPRequestOperationManager()
self.manager.POST(self.reservationsURL, parameters: reservation.parametersDictionary(), success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) -> Void in
var responseObject = (operation.responseObject as! Dictionary<String, AnyObject>)["data"] as! Dictionary<String, AnyObject>
// responseObject = ["source": 1,
// "name": "Rodolfo",
// "info": "",
// "id": 160,
// "created": "2015-08-24T21:00:29Z",
// "people": 3,
// "datetime": "2015-08-24T21:00:29Z",
// "status": 0,
// "mobile": "11999999000",
// "modified": "2015-08-24T21:00:29Z"]
self.realm.write { () -> Void in
self.realm.create(GIReservation.self, value: parsedData, update: true)
}
}) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in
println("OPS!\(operation.error)")
}
And this model on Realm:
public class GIReservation: Object{
dynamic var name = ""
dynamic var mobile = ""
dynamic var datetime = NSDate()
dynamic var people = 0
dynamic var status = 0
dynamic var info = ""
dynamic var id = 0
dynamic var localID = NSUUID().UUIDString
dynamic var user: User?
public func parametersDictionary() -> Dictionary<String, AnyObject> {
var parameters: [String : AnyObject] = ["name": self.name, "mobile": self.mobile, "datetime" : self.datetime.toUTCISO8601String(), "people" : self.people, "status" : self.status]
if self.info.isEmpty {
parameters["info"] = self.info
}
return parameters
}
public override static func primaryKey() -> String? {
return "localID"
}
}
The Problem:
Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value '3' for property 'people''
The same occurs on any other that isn't a String, so ok, I've checked that everything is returning a NCFString type, doesn't matter if it's a Int, or a Date.
Well the strange is; when I try the same thing with Objective-C and a RLMObject, it works like a charm.
The content-type of the JSON is application/json.
Any ideas what is happening?