Swift: Unable to get array values from singleton class - swift

Hi I retrived value from JSON and stored in NSMutableArray. I have tried this like Singleton. I have used empty swift file for this. Datas successfully retrieved and stored in NSMutableArray which is already declared in mainViewController. Then, if I use that NSMutableArray value in mainViewController, it shows empty array.
My coding is below. Kindly guide me.
Empty Swift File
public class json_file{
var prod_Obj = product_mainVC()
class var shared: json_file
{
struct Static
{
static let instance: json_file = json_file()
}
return Static.instance
}
func dataFromJSON()
{
let url = NSURL(string: "http://........--...4900a20659")!
var data : NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMapped, error: nil)!
var dict: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
let dataArray = dict["data"] as [[String:AnyObject]] // The array of dictionaries
for object in dataArray {
let category_name = object["category_name"] as String
prod_Obj.ct_name_arr.addObject(category_name)
let category_child = object["category_child"] as [[String:AnyObject]]
for child in category_child {
let sub_category_name = child["sub_category_name"] as String
prod_Obj.sub_ct_name_arr.addObject(sub_category_name)
}
}
println(prod_Obj.ct_name_arr) //Here value is Getting
println(prod_Obj.sub_ct_name_arr) //Here value is Getting
}
}
viewDidLoad
{
json_file.shared.dataFromJSON()
println(ct_name_arr) //Prints Empty Array [Intially Declared as NSMutableArray]
println(sub_ct_name_arr) //Prints Empty Array [Intially Declared as NSMutableArray]
}

I was trying understand the problem, but I can't see the product_mainVC. Because this I remake your class with little modifications.
class JsonFile
{
private(set) var categoryNames:[String];
private(set) var subCategoryNames:[String];
class let shared:JsonFile = JsonFile();
private init()
{
self.categoryNames = [];
self.subCategoryNames = [];
}
func dataFromJson()
{
let url = NSURL(string: "http://........--...4900a20659")!
if let data : NSData = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMapped, error: nil)
{
if let dict: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary
{
if let dataArray = dict["data"] as? [[String:AnyObject]] // The array of dictionaries
{
for object in dataArray {
let category_id = object["category_id"] as Int
let category_name = object["category_name"] as String
categoryNames.append(category_name);
let category_child = object["category_child"] as [[String:AnyObject]]
for child in category_child {
let sub_category_id = child["sub_category_id"] as Int
let sub_category_name = child["sub_category_name"] as String
subCategoryNames.append(sub_category_name);
}
}
}
}
}
println(categoryNames);
println(subCategoryNames);
}
}
I did
Modify your way to do Singleton to a safe and more simple mode, create the arrays categoryNames and subCategoryNames internal in class because this is better to manipulate, and protect your fetch data to safe from possibles crash.
Implementation
JsonFile.shared.dataFromJson();
println("count categoryNames");
println(JsonFile.shared.categoryNames.count);
println("count subCategoryNames");
println(JsonFile.shared.subCategoryNames.count);
You need think about
This code is sync, and because this if you have a big data or request slow, the main thread from your application will freeze waiting return and it is bad for your user. Think if is necessary be sync.
let category_id = object["category_id"] as Int is never used. Why do you stay with this in code?

Related

Using UISearchbar with JSON to filter the result

I am fetching the data from JSON using http in the following code:
I have an ObjectModel, DownloadModelProtocol, and TableViewController
(Modal.swift)
class OrderItemModal: NSObject {
var deptname: String!
var staffname: String!
var status: String!
var userid: String!
}
(DownloadOrderModal.swift):
protocol OrderDownloadProtocol: class {
func itemsDownload(items: Array<Any>)
}
...
let bmsOrders = NSMutableArray()
...
weak var delegate: OrderDownloadProtocol!
let urlPath = "http://localhost/production/api/db_orders.php"
func downloadItems() {
let url = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
...
for i in 0..<jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let bmsOrder = OrderItemModal()
....
bmsOrders.add(bmsOrder)
....
declaration:
var orderItems = [OrderItemModal]()
var filterArray= [OrderItemModal]()
func itemsDownload(items: Array<Any>) {
orderItems = items as! [OrderItemModal]
}
and viewDidLoad:
let bmsOrder = DownloadOrderModal()
bmsOrder.delegate = self
bmsOrder.downloadItems()
this is the JSON result:
(
{
"deptname" = "Production";
"staffname" = Warehouse;
"status" = 1;
"userid" = ware;
})
This the the search bar code
filterArray = orderItems.filter( { ($0. staffname) (of: searchText, options: .caseInsensitive) })
And finally, this is the error:
Cannot assign value of type '[OrderItemModal]' to type '[String]'
Ultimately, I will populate the data into a table.
You have a few issues. It seems that orderItems is an NSArray array of OrderItemModal values. The first thing you need to do is to stop using NSArray and use a Swift array of the proper type. In this case it should be [OrderItemModal]. You will need to ensure filterArray is also declared as [OrderItemModal].
The result of a filter on such an array will be an array of OrderItemModal but you are attempting to force cast the result as an array of String.
You are also force-casting the closure to be (Any) -> Bool. There's no need for that.
And lastly, you are needlessly using NSString. Stick with String.
All you need is:
filterArray = orderItems.filter { (item) -> Bool in
return item.staffname.range(of: searchText, options: .caseInsensitive) != nil
}
Even simpler:
filterArray = orderItems.filter { $0.staffname.range(of: searchText, options: .caseInsensitive) != nil }

How to compare two values in Realm Swift

I have a Realm database called NewsCount. I need to download a new news only if there is a new news (respectively when newsCount change). And I make a comparison with the data parsing. But I can not compare them properly. How do you compare them?
Thi is my code
private func parseJSONData(_ data: Data) {
do {
let temp: NSString = NSString(data: data, encoding: String.Encoding.utf8.rawValue)!
let myNSData = temp.data(using: String.Encoding.utf8.rawValue)!
guard let jsonResult = try JSONSerialization.jsonObject(with: myNSData, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary else {
return
}
guard let jsonNews = jsonResult["categories"] as? [AnyObject] else {
print("Empty array")
return
}
let realm = try Realm()
let category = realm.objects(NewsCount.self)
var array = [Int]()
for i in category {
array.append(i.newsCount)
}
print(array)
print("News COUNT2 \(category)")
for jsonnewes in jsonNews {
let newsJson = NewsCount()
//HERE I COMPARE
if !UserDefaults.standard.bool(forKey: "AppStarted") || jsonnewes["count"] as! Int > array[jsonnewes as! Int]{
newsJson.newsID = jsonnewes["term_id"] as! Int
newsJson.newsCount = jsonnewes["count"] as! Int
//print("News COUNT2 \(newsJson.newsCount)")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "downloadNew"), object: nil)
} else {
newsJson.newsID = jsonnewes["term_id"] as! Int
newsJson.newsCount = jsonnewes["count"] as! Int
//print("News COUNT3 \(newsJson.newsCount)")
}
insertOrUpdate(newsJson)
}
} catch {
print(error)
}
}
Because Realm uses RealmOptional to use Int values you have to call the value atribute to the RealmOptional
Try change this:
for i in category {
array.append(i.newsCount.value)
}
First off, it's probably more appropriate to use Int(string) instead of as! Int force-casting to convert your JSON data to integers.
From the looks of it, jsonnewes is a dictionary full of JSON data, but you're casting it as an array index in array[jsonnewes as! Int] which (given array is an array and not a dictionary) shouldn't work.
Instead, in order to make sure you're explicitly retrieving the item you want, I'd recommend using Realm's primary key query method in order to retrieve the item you want.
let realm = try! Realm()
for newsItem in jsonNews {
let newsPrimaryKey = Int(newsItem)
let realmNews = realm.object(ofType: NewsCount.self, forPrimaryKey: newsPrimaryKey)
// Don't continue if a Realm object couldn't be found
guard let realmNews = realmNews else {
continue
}
// Perform comparison
if Int(newsItem["count"]) > realmNews.newsCount {
// Perform the update
}
}

Property declared in my class isn't recognized when attempting to use it inside a function?

I've checked for the misspelling of the property, that's definitely not the case. I'm trying to use the property mySong that I declared in my class inside the parseSongs() function.
That function isn't inside the class but it's in the same file. And the target membership of that class is set to the project name as are the other files as well.
I'm very confused why the compiler isn't recognizing the name of my property in the parseSongs()?
I can declare the property outside of the class but I should be able to use it even if it's declared inside the class.
import UIKit
class SongsTableViewController: UITableViewController {
//A property that is an array of type 'Song'
var mySong = [Song]()
private let cache = NSCache()
private func fetchMyData(){
let myUrl = NSURL(string: "http://itunes.apple.com/search?term=beatles&country=us")!
let mySession = NSURLSession.sharedSession()
//The work to be queued initiates
let myTask = mySession.dataTaskWithURL(myUrl){
//This closure right here is the Completion Handler
data, response, error in
if error != nil{
//Handle error
}else{
let myHttpResponse = response as! NSHTTPURLResponse
switch myHttpResponse.statusCode {
case 200..<300:
print("OK")
print("data: \(data)")
default: print("request failed: \(myHttpResponse.statusCode)")
}
}
}
myTask.resume()
}
}
func parseJson(myData data: NSData){
do{
let json: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let unwrappedJson: AnyObject = json{
parseSongs(unwrappedJson)
}
}catch{
}
}
func parseSongs(json1: AnyObject){
mySong = []
//Optional Binding
if let array = json1["results"] as? [[String:AnyObject]]{
//For-In loop
for songDictionary in array{
if let title = songDictionary["trackName"] as? NSString{
if let artist = songDictionary["artistName"] as? NSString{
if let albumName = songDictionary ["collectionName"] as? NSString{
if let artWorkUrl = songDictionary["artWorkUrl100"] as? NSString {
let song = Song(artist: (artist as String), title: (title as String), albumName: (albumName as String), artWorkUrl: (artWorkUrl as String))
mySong.append(song)
}
}
}
}
}
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
}
}
To use the property outside which declared inside a class you have to follow this
SongsTableViewController().theProperty
If you declare it outside class then you can access it in function of outside class

Cannot convert value of type 'String?!' to expected argument type 'Notifications'

I am trying to check the id of a record before I put it into the array, using xcode swift
here is the code. But, i get the following error
Notifications.swift:50:46: Cannot convert value of type 'String?!' to expected argument type 'Notifications'
on this line
*if (readRecordCoreData(result["MessageID"])==false)*
Please can some one help to explain this error
import CoreData
struct Notifications{
var NotifyID = [NSManagedObject]()
let MessageDesc: String
let Messageid: String
init(MessageDesc: String, Messageid:String) {
self.MessageDesc = MessageDesc
self.Messageid = Messageid
// self.MessageDate = MessageDate
}
static func MessagesWithJSON(results: NSArray) -> [Notifications] {
// Create an empty array of Albums to append to from this list
var Notification = [Notifications]()
// Store the results in our table data array
if results.count>0 {
for result in results {
//get fields from json
let Messageid = result["MessageID"] as! String
let MessageDesc = result["MessageDesc"] as? String
let newMessages = Notifications(MessageDesc: MessageDesc!, Messageid:Messageid)
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
}
}
return Notification
}
//check id
func readRecordCoreData(Jsonid: String) -> Bool {
var idStaus = false
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "ItemLog")
//3
do {
let resultsCD = try! managedContext.executeFetchRequest(fetchRequest)
if (resultsCD.count > 0) {
for i in 0 ..< resultsCD.count {
let match = resultsCD[i] as! NSManagedObject
let id = match.valueForKey("notificationID") as! String
if (Jsonid as String! == id)
{
idStaus = true
}
else{
idStaus = false
}
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
return idStaus
}
One of your methods is static and the other one is not :
func readRecordCoreData(Jsonid: String) -> Bool
static func MessagesWithJSON(results: NSArray) -> [Notifications]
Depending on what you want to accomplish you could declare both static, none, or replace
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
By
//check with id's from core data
if (Notifications.readRecordCoreData(Messageid)==false)
{
Notification.append(newMessages)
}
Not sure if the code will work past compilation however as there are many readability issues

Saving an array of NSURL to NSUserDefaults

I have an array of NSURL items like this:
var array = [NSURL]()
I'm trying to save this to NSUserDefaults:
let defaults = NSUserDefaults(suiteName: "group.a.project")
defaults?.setObject(array, forKey: "NSURLarray")
Will result in it crashing as NSURL can't be stored in NSUserDefaults.
How can I save this array of NSURL's to NSUserDefaults, so that I can then access it and use the NSURL's in the array as required (saving an array of strings and then trying to use them as NSURL's will result in an error from xcode)?
You could archive your URL array to an instance of NSData and store that in user defaults. To save:
let urls = [ NSURL(string: "http://www.google.com")!, NSURL(string: "http://www.stackoverflow.com")! ]
let defaults = NSUserDefaults(suiteName: "group.a.project")
let data = NSKeyedArchiver.archivedDataWithRootObject(urls)
defaults?.setValue(data, forKey: "myUrls")
To load:
if let data = defaults?.valueForKey("myUrls") as? NSData {
if let urlsArray = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [NSURL] {
println(urlsArray)
}
}
when saving, save the absolute strings of the urls. when loading use NSURL(URLString:) to make the strings into urls again
import UIKit
class ViewController: UIViewController {
var urlsArray : [NSURL]?
func load () {
var urls : [NSURL] = []
let stringsArray = NSUserDefaults.standardUserDefaults().objectForKey("stringsArray") as [String]?
if let array = stringsArray {
for string in array {
var url = NSURL(string: string)
urls.append(url!) //no null check
}
}
self.urlsArray = urls
}
func save () {
var strings : [String] = []
if let array = self.urlsArray {
for url in array {
var string = url.absoluteString
strings.append(string!) ////no null check
}
}
NSUserDefaults.standardUserDefaults().setObject(strings, forKey: "stringsArray")
}
}
saving can be reduced with KVC.
func save () {
var strings : [String] = []
if let array = self.urlsArray as NSArray? {
strings = array.valueForKeyPath("absoluteStrings")
}
NSUserDefaults.standardUserDefaults().setObject(strings, forKey: "stringsArray")
}
This could be a bit more swifty:
func load () {
if let stringsArray = NSUserDefaults.standardUserDefaults().objectForKey("stringsArray") as? [String] {
self.urlsArray = stringsArray.compactMap { URL(string: $0) }
}
}
func save () {
NSUserDefaults.standard.setObject(self.urlsArray.map { $0.absoluteString }, forKey: "stringsArray")
}