Variable value get empty in iOS swift - ios

I have create computed property for store the strTokenValue and send it to the web services.
private var strTokenValue = String()
var tokenValue: String {
get {
if strTokenValue != "" {
return strTokenValue
}
else {
if let token = StrongBoxController.sharedInstance.keychainStore.unarchive(objectForKey: "TokenValue") as? Data {
strTokenValue = StrongBoxController.sharedInstance.convertDataToString(value: token)
return strTokenValue
}
else {
return ""
}
}
}
set {
strTokenValue = ""
StrongBoxController.sharedInstance.saveValues(value: "\(newValue)", key: “TokenValue”)
}
}
We have send every user action Asynchronously and we have not wait for success response for track function web services, So i have not added success and failure response in the below code.
func track(actionTaken: String,incidentNumber: String,message: String,completion:#escaping(_ success: Bool, _ error: String,_ actionTakenName: String) -> Void ) {
DispatchQueue.global(qos: .background).async {
let url = "\(ServerCommunication.sharedInstance.getDomainBaseUrl())api/track"
let parameters = [
“token_value” : self.tokenValue,
"device_os":"iOS",
"device_type": UIDevice.modelName,
"action_taken": actionTaken,
"message" : message,
]
}
}
We have called the one more web service to send the image and tokenValue.
Request:
“token_value” : “”,
"device_os":"iOS",
"device_type": “iPhone X”,
"action_taken": “Image captured”,
"message" : “”,
func convertDataToString(value: Data) -> String {
return String(decoding: value, as: UTF8.self)
}
public func unarchive(objectForKey key:String) -> Any? {
guard let data = self.data(forKey: key) else {
return nil
}
let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
return unarchiver.decodeObject(forKey: key)
}
#objc func saveValues(value : String, key: String) {
_ = self.keychainStore.archive(convertStringToData(value: value), key: key, accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
}
Both web services called simultaneously and “tokenValue” get empty in trackActions web services in some scenarios

It seems the problem is that you save data using convertStringToData which doesn't probably use convertStringToData. I think so because of convertDataToString doesn't use NSKeyedAarchiver.
In order to resolve, you should use unarchive.
To save the data use
func convertDataToString(value: Data) -> String? {
return String(data: value, encoding: .utf8)
}
func convertStringToData(value: String) -> Data? {
return value.data(using: .utf8)
}
and
keychainStore.archive(convertStringToData(value: value), key: key, accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
stored = convertDataToString(StrongBoxController.sharedInstance.keychainStore.unarchive(objectForKey: "TokenValue"))

Related

SwifityJSON problem with parsing JSON to ViewController from other libraries/frameworks

I have the following json look like:
"message": "Something message here"
"data": [
{ "_id": "671672167bhw2hw"
"photo": "https://uploads.example.com/671672167bhw2hw.jpg"
"description": "Something description here"
"date": "2020-07-01T09:44:15.448Z"
"user": "John Doe"
}
]
}
I used SwiftifyJSON library to decode and parsing JSON. My code is look like:
class Post: NSObject {
var message: String
var text: String?
var photoURL: URL?
var date: Date?
}
init(json: JSON) {
self.user = User(id: json["user_id"].stringValue,
name: json["user"].stringValue,
photoURL: URL(string: json["user_photo"].stringValue))
self.message = json["message"].stringValue
self.objectId = json["data"]["_id"].stringValue
self.text = json["data"]["description"].string
self.date = DateFormatter.iso8601.date(from: json["data"]["date"].stringValue)
}
and my ApiManager extensions
static func getPosts(before date: Date? = nil, withSuccess success: #escaping (Bool, [Post]) -> Void) {
guard let url = URL(string: ApiEndpoint.Wall.getPosts) else {
success(false, [Post]())
return
}
var json = [String: String]()
DispatchQueue.global(qos: .background).async {
ApiManager.request(url: url, method: .post, parameters: json) { response, statusCode in
switch statusCode {
case 200:
success(true, ApiManager.getWallPostsFrom(response: response))
return
default: break
}
success(false, [Post]())
return
}
}
}
private static func getPostsFrom(response: DataResponse<Any>) -> [Post] {
var result = [Post]()
guard let value = response.result.value,
let jsonArray = JSON(value).array else { return result }
for json in jsonArray {
let object = Post(json: json)
result.append(object)
}
return result
}
}
I get response with 200, it's OK.
But still not working for displaying data in PostViewController. What is wrong? Additionaly I use my own libraries for tabs in application like Contacts, Messages, Discussion Panel and more, so I use my own library with some files, models, views, assets for any tab.
Below is my PostViewController for displaying data from JSON.
private func tryGetPosts() {
canLoadPosts = false
tableView.dataState = .downloading
ApiManager.getPosts(before: Posts.last?.date) { [weak self] (success, objects) in
self?.tableView.dataState = .noData
print("Objects count: ", objects)
guard objects.count > 0 else { return }
#warning("Insert animation")
self?.tableView.beginUpdates()
self?.Posts += objects
let indexPaths = (self!.Posts.count - objects.count ..< self!.Posts.count)
.map { IndexPath(row: $0, section: 0) }
self?.tableView.insertRows(at: indexPaths, with: .top)
self?.tableView.endUpdates()
self?.Posts.insert(contentsOf: objects, at: self?.Posts.count ?? 0)
self?.tableView.reloadData()
self?.canLoadWallPosts = true
}
Please for any reply and advice.
Best regards.
The answer for this problem was in JSON(value).array.
If your JSON structure have like "data" array you can write JSON(value)["data"].array. SwiftyJSON is really friendly and easy library.

Swift - How to write complex objects to Firebase realtime database

I'm new to the Firebase realtime database and relatively new to Swift in general. I am attempting to build a song request app in which users can create events for guests to request songs from the Spotify API. I'm trying to write an Event object to Firebase, which contains nested objects and arrays of different types. However, when it writes to the database, it only writes the strings and none of the arrays or objects. What is the best way to write all this information to the Firebase Database in a nested structure, so that whenever users add song requests, I can edit the array of requests for the given event in firebase.
Here is my code:
Event.swift
struct Event: Codable{
var code: String
var name: String
var host: String
var description: String
var hostUserId: String
var guestIds: [String]
var requests: [Request]
var queue: [Request]
var played: [Request]
//private var allowExplicit: Bool
//private var eventLocation
init(code: String, name: String, host: String, description: String, hostUserId: String){
self.code = code
self.name = name
self.host = host
self.description = description
self.hostUserId = hostUserId
self.guestIds = []
self.requests = []
self.queue = []
self.played = []
}
func toAnyObject() -> Any{
var guestIdsDict: [String:String] = [:]
for id in guestIds{
guestIdsDict[id] = id
}
var requestsDict: [String: Any] = [:]
for request in requests{
requestsDict[request.getId()] = request.toAnyObject()
}
var queueDict: [String: Any] = [:]
for request in queue{
queueDict[request.getId()] = request.toAnyObject()
}
var playedDict: [String: Any] = [:]
for request in played{
playedDict[request.getId()] = request.toAnyObject()
}
return [
"code": code,
"name": name,
"host": host,
"description": description,
"hostUserId": hostUserId,
"guestIds": guestIdsDict,
"requests": requestsDict,
"queue":queueDict,
"played":playedDict
]
}
}
Request.swift
struct Request: Codable{
private var name: String
private var id: String
private var explicit: Bool
private var album: Album
private var artists: [Artist]
private var likes: Int
init(name: String, id: String, explicit: Bool, album: Album, artists: [Artist]){
self.name = name
self.id = id
self.explicit = explicit
self.album = album
self.artists = artists
self.likes = 1
}
func toAnyObject() -> Any{
var artistsDict: [String:Any] = [:]
for artist in artists {
artistsDict[artist.id] = artist.toAnyObject()
}
return [
"name": name,
"id": id,
"explicit": explicit,
"album": album.toAnyObject(),
"artists": artistsDict,
"likes": likes
]
}
mutating func like(){
self.likes += 1
}
mutating func unlike(){
self.likes -= 1
if(self.likes < 0){
self.likes = 0
}
}
mutating func setLikes(count: Int){
self.likes = count
}
func getLikes() -> Int{
return self.likes
}
func getName() -> String{
return self.name
}
func getId() -> String{
return self.id
}
func getExplicit() -> Bool{
return self.explicit
}
func getAlbum() -> Album {
return self.album
}
func getImages() -> [Image] {
return self.album.images
}
func getArtists() -> [Artist] {
return self.artists
}
func getArtistString() -> String{
var artistString = ""
for (i, artist) in self.artists.enumerated(){
artistString += artist.name
if(i != self.artists.endIndex-1){
artistString += ", "
}
}
return artistString
}
}
Album.swift
struct Album: Codable{
let name: String
let images: [Image]
func toAnyObject() -> Any{
var imagesDict: [String: Any] = [:]
for image in images{
imagesDict[image.url] = image.toAnyObject()
}
return [
"name": name,
"images": imagesDict
]
}
}
Artist.swift
struct Artist: Codable{
let id: String
let name: String
func toAnyObject() -> Any{
return ["id": id, "name": name]
}
}
Image.swift
struct Image: Codable{
let height: Int
let url: String
let width: Int
func toAnyObject() -> Any{
return ["height": height, "url": url, "width": width]
}
}
As you are using Codable, you can create a dic out of it as follows:
Step 1: Add this extension to your code
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
Step 2: Write below code in your Struct (this you have to do in every struct or you can modify code as per your need).
func createDic() -> [String: Any]? {
guard let dic = self.dictionary else {
return nil
}
return dic
}
Now with the help of struct obj, call createDic() method and you will get a dictionary.
And you can send this dictionary to the firebase.
FULL CODE EXAMPLE:
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
}
struct LoginModel: Codable {
let email: String
let password: String
func createDic() -> [String: Any]? {
guard let dic = self.dictionary else {
return nil
}
return dic
}
}
Please comment if you have any questions.
Happy to help!

Can't access model object data outside the alamofire request scope while populating api data on badoo/Chatto chat text

Previously I successfully access model objects anywhere in the class but while populating data on badoo/chatto text view I am stuck.
I am integrating chat message api into badoo chat view
Basically, the issue is alamofire response is not getting outside of the scope.
Did I try with compilation handler but no luck? Is there any way to resolve this issue?
Thanks in advance .
Here is code snippet :
import Foundation
import Chatto
import ChattoAdditions
import SwiftyJSON
import Alamofire
class DemoChatMessageFactory {
public static var chats = [ChatModel]()
class func makeMessage(_ uid:String) -> DemoTextMessageModel{
print("uid makeMessage : \(uid)")
return self.makeMessageData(uid, isIncoming:false)
}
class func makeMessageData(_ uid: String,isIncoming:Bool) -> DemoTextMessageModel {
if isIncoming == true{
return self.makeTextFinalMessage(uid, isIncoming:isIncoming)
} else {
return self.makeTextFinalMessage(uid, isIncoming: isIncoming)
}
}
public class func makeTextMessage(_ uid: String, isIncoming: Bool,text:String) -> DemoTextMessageModel {
let messageModel = self.makeMessageModel(uid, isIncoming: isIncoming,
type: TextMessageModel<MessageModel>.chatItemType)
let textMessageModel = DemoTextMessageModel(messageModel:messageModel,
text: text)
return textMessageModel
}
public class func makeTextFinalMessage(_ uid: String, isIncoming: Bool) -> DemoTextMessageModel {
var text = String()
var uidInt = Int(uid)
print("string uid 121 \(uid)")
print("print is Incomming data or not 1: \(isIncoming)")
print("uid count :\(uid.count)")
let urlString = "[My message Api]"
Alamofire.request(urlString, method: .get).validate().responseJSON {
(response) -> Void in
if let value = response.data {
do {
let json = try JSON(data: value)
if let dictionnary = json.dictionaryObject {
if let messageArray = dictionnary["message"] as?[[String: Any]] {
self.chats.removeAll()
for arr in messageArray {
self.chats.append(ChatModel(ChatListJSON: arr))
}
}
}
} catch {
print("cannot convert to Json")
}
}
print("print int 122 : \(uidInt!)")
print("Chat List Id DemoChatMessageFactory \(self.chats[uidInt!].chatId)")
print("chat message: \(String(describing: uidInt!)) th \(self.chats[uidInt!].chatMessage)")
self.textData = "\(self.chats[uidInt!].chatMessage)"
self.makeTextMessage(uid, isIncoming: isIncoming, text:self.textData) //Here I am bale to pass textData but ouside the Alamofire block can't access
}
//Here getting empty values
print("uid makeTextFinalMessage \(uid)")
print("in coming makeTextFinalMessage \(isIncoming)")
print("text makeTextFinalMessage \(text)")
//chat count also getting zero count
print("chat count final text\(chats.count)")
print("print chat count : \(self.chats.count)")
return self.makeTextMessage(uid, isIncoming: isIncoming, text:self.textData)
}
}
Test for completion handler
public var res: Any = ""
func getAllChatData(completionhandler:#escaping ([String: Any]?) -> ()){
let URL = "my api"
Alamofire.request(URL).responseJSON {
response in
if let json = response.result.value as? [String: Any] {
completionhandler(json, nil)
}
else if let error = response.result.error as Error? {
completionhandler(nil, error)
}
}
}
and call using like below inside the function
DemoChatMessageFactory.getAllChatData {
(result) in
res = result
print("response (res)")
}
please suggest me the proper way to alamofire with compilation handler
This is an example of converting all methods using the result of asynchronous call. As I have never used Chatto and you are not showing all the types in your code, so you may need to modify many parts of my code, but I believe you can see what you need to do with this code.
import Foundation
import Chatto
import ChattoAdditions
import SwiftyJSON
import Alamofire
class DemoChatMessageFactory {
public static var chats = [ChatModel]()
class func requestMessage(_ uid:String,
completion: #escaping (DemoTextMessageModel?, Error?)->Void) {
print("uid makeMessage : \(uid)")
self.requestMessageData(uid, isIncoming: false) { (model, error) in
completion(model, error)
}
}
class func requestMessageData(_ uid: String, isIncoming: Bool,
completion: #escaping (DemoTextMessageModel?, Error?)->Void) {
if isIncoming {
//...put any code needed when isIncoming is true
self.requestTextFinalMessage(uid, isIncoming: isIncoming) { model in
completion(model, error)
}
} else {
//...put any code needed when isIncoming is false
self.requestTextFinalMessage(uid, isIncoming: isIncoming) { model in
completion(model, error)
}
}
}
public class func makeTextMessage(_ uid: String, isIncoming: Bool, text: String) -> DemoTextMessageModel {
let messageModel = self.makeMessageModel(uid, isIncoming: isIncoming,
type: TextMessageModel<MessageModel>.chatItemType)
let textMessageModel = DemoTextMessageModel(messageModel:messageModel,
text: text)
return textMessageModel
}
public class func requestTextFinalMessage(_ uid: String, isIncoming: Bool,
completion: #escaping (DemoTextMessageModel?, Error?)->Void) {
var text = String()
var uidInt = Int(uid)
print("string uid 121 \(uid)")
print("print is Incomming data or not 1: \(isIncoming)")
print("uid count :\(uid.count)")
let urlString = "[My message Api]"
Alamofire.request(urlString, method: .get).validate().responseJSON {
(response) -> Void in
if let value = response.data {
do {
let json = try JSON(data: value)
if let dictionnary = json.dictionaryObject {
if let messageArray = dictionnary["message"] as?[[String: Any]] {
self.chats.removeAll()
for arr in messageArray {
self.chats.append(ChatModel(ChatListJSON: arr))
}
}
}
print("print int 122 : \(uidInt!)")
print("Chat List Id DemoChatMessageFactory \(self.chats[uidInt!].chatId)")
print("chat message: \(String(describing: uidInt!)) th \(self.chats[uidInt!].chatMessage)")
self.textData = "\(self.chats[uidInt!].chatMessage)"
completion(self.makeTextMessage(uid, isIncoming: isIncoming, text: self.textData), nil)
} catch {
print("cannot convert to Json")
completion(nil, error)
}
} else {
//better generate an error case result, and call completion.
//...
}
}
}
}
I changed some method names from make... to request... to show clarify they are asynchronous methods.
And the usage, if you intend to use your original code as:
let model = DemoChatMessageFactory.makeMessage(uid)
//Do some UI updates using `model`...
You may need to use asynchronous methods like:
DemoChatMessageFactory.requestMessage(uid) { (model, error) in
if let model = model {
//Do some UI updates using `model`...
} else {
//Do something for the error...
}
}

How to Convert a model object into JSON - Swift 4

How to Convert a model object into JSON?
I want "Answer" object.
// Answers Class
class Answers {
var cat_id: String!
var responses = [Response]()
var date: String!
var comment: String!
var time: String!
var lat: String!
var lon: String!
var address: String!
}
// Response Class
class Response {
var que_id: String!
var question: String!
var response: String!
}
Make both types conform to Codable:
class Answers: Codable {
...
}
class Response: Codable {
...
}
And then use JSONEncoder:
let answers: Answers = ...
do {
let data = try JSONEncoder().encode(answers)
// use data here
} catch {
print(error)
}
See Encoding and Decoding Custom Types.
if you are using swift4, You can use encodable and decodable protocol. I'm still working on a heterogeneous list of objects. But this should work for you. Make your class conform to ABEncodable.
protocol ABDecodable: Decodable {
static func decodeFromData(_ data: Data) -> Decodable?
}
protocol ABEncodable: Encodable {
static func encodeFromObject<T>(_ object: T) -> Data? where T: Encodable
}
extension ABDecodable {
static func decodeFromData(_ data: Data) -> Decodable? {
do {
return try JSONDecoder().decode(self, from: data)
}
catch {
print(error)
}
return nil
}
}
extension ABEncodable {
static func encodeFromObject<T>(_ object: T) -> Data? where T: Encodable {
do {
return try JSONEncoder().encode(object)
}
catch {
return nil
}
}
}
//MARK: Decode mapper class
//Send jsonString or data to decode it into an required Object
final class Decode<T: Decodable> {
private func decodeData(_ data: Data) -> T? {
if let klass = T.self as? ABDecodable.Type {
if let object = klass.decodeFromData(data) as? T {
return object
}
}
else {
do {
return try JSONDecoder().decode(T.self, from: data)
}
catch {
print(error)
}
}
return nil
}
func fromJsonString(_ json: String) -> T? {
guard let data = json.data(using: String.Encoding.utf8) else { return nil }
if let object = decodeData(data) {
return object
}
return nil
}
func fromData(_ data: Data) -> T? {
if let object = decodeData(data) {
return object
}
return nil
}
}
//MARK: Encode mapper class
//Send jsonString or data to decode it into an required Object
final class Encode<N:Encodable> {
private func encodeObject(_ object: N) -> Data? {
if let klass = N.self as? ABEncodable.Type {
if let data = klass.encodeFromObject(object) {
return data
}
}
else {
do {
return try JSONEncoder().encode(object)
}
catch {
print(error)
}
}
return nil
}
func toJsonString(_ object: N) -> String? {
if let data = encodeObject(object) {
return String(data: data, encoding: .utf8)
}
return nil
}
func toData(_ object: N) -> Data? {
if let data = encodeObject(object) {
return data
}
return nil
}
}

IGListKit insert data to class from Alamofire request

So i have this model
class Event: NSObject {
var _eventName: String!
var _venueName : String!
var _eventImage: String!
var eventName: String {
if _eventName == nil {
_eventName = ""
}
return _eventName
}
var venueName: String {
if _venueName == nil {
_venueName = ""
}
return _venueName
}
var eventImage: String {
if _eventImage == nil {
_eventImage = ""
}
return _eventImage
}
init(eventsDict: Dictionary<String, AnyObject>) {
if let venue = eventsDict["venue"] as? Dictionary<String, AnyObject> {
if let venuname = venue["name"] as? String{
self._venueName = venuname
}
if let eventname = eventsDict["name"] as? String {
self._eventName = eventname
}
if let eventimage = eventsDict["coverPicture"] as? String {
self._eventImage = eventimage
}
}
}
And i make it IGListDiffable with this extension.
extension NSObject: IGListDiffable {
public func diffIdentifier() -> NSObjectProtocol {
return self
}
public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
return isEqual(object)
}
}
So when I'm loading data from hardcoded code like this
var entries = [Event]()
func loadFakeEvents() {
let entries = [
Event(
eventName: "Ζωρζ Πιλαλι Και Η Soufra Band Στο AN Groundfloor - Live Stage!",
venueName: "AN Groundfloor - live stage",
eventImage: "https://scontent.xx.fbcdn.net/v/t31.0-8/s720x720/15936729_1867160333520142_8855370744955080264_o.jpg?oh=8198bc10a8ea61011d7ec1902b34aa01&oe=593D6BC4"
),
Event(
date: "2017-02-18T21:30:00+0200",
name: "Διονύσης Σαββόπουλος at Gazarte I Main Stage 18/02",
venuename: "Gazarte",
eventImage: "https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/16265335_1262826863809003_3636661375515976849_n.jpg?oh=5bb342321a65d33dbc1cc41de266b45e&oe=5907857C"
)
]
self.entries = entries
}
The events are loading fine. As they have to.
But when i'm making an alamofire request, of course, it takse some time to load the data and append them to the empty array of events.
This is the function that I have to call the events
func loadEvents() {
let parameters: Parameters = [
"Some" : "Parameters",
"Some" : "Parameters"
]
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
print(dict) // <-- Check this out
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
}
}
}
}
}
So in the above code i have a print, which prints the json.
And in my
extension LocationViewController: IGListAdapterDataSource {
func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
let items: [IGListDiffable] = loader.entries as [IGListDiffable]
print(items.count) // <--- another print of items that should be displayed
return items
}
func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
return NormalSectionController()
}
func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil }
}
Adapter i also print the items that should be displayed.
So when i load the fakeEvents function it prints 2 but when i load them with the normal function it prints 0 and then the JSON from the dict var from the previous code.
Normally i would reloadData() of the collection view.
But with IGListKit what is the trick of sending the Event Class to the CollectionView?
Thanks a lot for your time and i hope i'm not off topic !
Pasting my answer from this same issue on Github in case anyone finds this.
https://github.com/Instagram/IGListKit/issues/468
It looks like you're missing a call to self.adapter.performUpdates(animated: true) after the for-loop when appending to the entries dict:
func loadEvents() {
// ...
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if responseData.result.value != nil {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
// missing this!
self.adapter.performUpdates(animated: true)
// missing that!
}
}
}
}
}

Resources