In my swift code below I am trying to save a string into core data using a helper class.
Right now my code is causing a runtime error stating Cannot assign value of type 'Data' to type 'String?' at imageInstance.text = data. I did something similar trying to save a image and it worked. I also added a core data photo
core data pic
class DataBaseHelper {
static let shareInstance = DataBaseHelper()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveText(data: Data) {
let imageInstance = Info(context: context)
imageInstance.txt = data
do {
try context.save()
print("text is saved")
} catch {
print(error.localizedDescription)
}
}
}
BASE CLASS
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
DataBaseHelper.shareInstance.saveText(data: "Jessica")}
Please be more careful how you name your functions.
You are going to save Text( so the parameter label data is misleading and the type Data is wrong.
Replace
func saveText(data: Data) {
with
func saveText(data: String) {
or – more meaningful because text is already a part of the function name
func saveText(_ string: String) {
let imageInstance = Info(context: context)
imageInstance.txt = string
and call it
DataBaseHelper.shareInstance.saveText("Jessica")
Related
In my swift code below the goal is to delete the first item in the core data boolean attribute. My code is not working below but its close to what I used to successfully fetch a boolean value. I don't really know what to add. There is a base class and a helper class.
class ViewController: UIViewController {
func deleteBool(imageNo:Int) {
// first check the array bounds
let info = helpBool.shareInstance.fetchBool()
if info.count > imageNo {
}
}
override func viewDidLoad() {
super.viewDidLoad()
deleteBool(imageNo: 1)}}
HELPER CLASS
class helpBool: UIViewController{
static let shareInstance = helpBool()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func deleteBool(_ boolean: Bool) {
let imageInstance = OnOff(context: context)
imageInstance.bool = boolean
do {
try context.delete()
print("text is saved")
} catch {
print(error.localizedDescription)
}
}
I have issue with implementing data passing between two WatchKit interface controllers. I want to pass data from first interface controller to another one. The first interface controller is working fine without any issue, but the second interface controller not getting the data from the first one.
Here is my struct :
struct gameStruct: Codable {
var id: String
var image: String
var gameName: String
var gameDate: String
var gameVideo: String
var gameSite: String
}
Here is the code in first interface controller:
var gameWatchArray = [gameStruct]()
let getDataURL = "http://ya-techno.com/gameApp/gameData.php"
class InterfaceController: WKInterfaceController {
#IBOutlet var tableView: WKInterfaceTable!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
let URLgame = URL(string: getDataURL)
URLSession.shared.dataTask(with: URLgame!) { (data, response, error) in
do {
guard let data = data else { return }
gameWatchArray = try JSONDecoder().decode([gameStruct].self, from: data)
} catch {
print("error parse")
}
DispatchQueue.main.async {
self.tableView.setNumberOfRows(gameWatchArray.count, withRowType: "gameRow")
for (gameNameIndex, game) in gameWatchArray.enumerated() {
let row = self.tableView.rowController(at: gameNameIndex) as! gameRow
let url = NSURL(string: "http://www.ya-techno.com/gamesImage/\(game.image)")
guard let data = NSData(contentsOf: url! as URL) else { return }
row.gameImage.setImageData(data as Data)
}
for index in gameWatchArray {
index.gameName
index.gameDate
index.image
print("JSON V3 array is :\(index.gameName)")
}
print("JSON V3 array is :\(gameWatchArray.count)")
}
}.resume()
}
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
self.pushController(withName: "showDetails", context: gameWatchArray[rowIndex])
}
and here is the Detail interface in my project:
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let detailData = context as? String {
gameNameLabel.setText(detailData)
}
}
I'm using json to parse data.
The issue is that you are passing a gameStruct instance using self.pushController(withName: "showDetails", context: gameWatchArray[rowIndex]) from your first interface controller to your second one, but then you are trying to cast gameStruct to String, which will obviously fail.
You should modify awake(withContext:) in your second interface controller to conditionally cast context to gameStruct and then access the gameName property of that struct when assigning the String name to the label's text.
In the future you should always handle the cases when a conditional casting fails so that you can find issues more easily by printing a message in case of a failed cast. Or if you are 100% sure that context will always be of a certain type, you can even do force casting, which will enable you to catch programming errors early on in the development stage.
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let gameDetails = context as? gameStruct {
gameNameLabel.setText(gameDetails.gameName)
} else {
print("Passed context is not a gameStruct: \(context)")
}
}
You should also conform to the Swift naming convention, which is UpperCamelCase for type names, so change gameStruct to GameStruct.
I´ve a webrequest with jsonserialization, after that, a for-in fetch process.
In whole this takes approximately 5-7 seconds.
After that i want to refersh my tableview in Viewcontroller.
The scheme of the function looks like this.
public struct Container {
let name: String
let symbol: String
let rank: String
}
public var dataArray = [Container]()
func fetchNewData() {
var view = ViewController()
// WebbRquest...
// Json serialization...
// the following list is much longer, will take a while...
for items in json {
let name = items["name"] as? AnyObject;
let symbol = items["symbol"] as? AnyObject;
let rank = items["rank"] as? AnyObject;
let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)
dataArray.append(result)
}
// Now, after alle the work is done, i want to reload the tableview in Viewcontrller:
view.reload()
// Here i´m getting error, because nothing will be executed after return.
}
How can I call the reload function, after the webrequest process is finished? Because after the return, the function doesn´t execute anything anymore.
And no other function will "know" when the fetchNewData() function is finished.
Thanks for any help!
#IBAction func updateButton(_ sender: Any) {
fetchNewData()
}
According Phillipps suggestion, I had to modify the #IBAction func a little bit.
But now it´s working. Awesome!
Here the full working version:
public struct Container {
let name: String
let symbol: String
let rank: String
}
public var dataArray = [Container]()
func fetchNewData(completion:#escaping ([Container])->()) {
var view = ViewController()
// WebbRquest...
// Json serialization...
// the following list is much longer, will take a while...
for items in json {
let name = items["name"] as? AnyObject;
let symbol = items["symbol"] as? AnyObject;
let rank = items["rank"] as? AnyObject;
let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)
dataArray.append(result)
}
completion(dataArray)
}
This is the actionFunc:
#IBAction func upDateButton(_ sender: Any) {
let data = dataArray
fetchNewData() {_ in (data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Here's a start. It will be vague because I'm making guesses about code I can't see, but you may be able to convert it to your own needs.
Change the fetch function so that it takes a closure as a parameter:
func fetchNewData(completion:([Container])->()) {
...note that the closure will accept the data array when it's called.
After you have your json all parsed, you then invoke the closure:
dataArray.append(result)
}
completion(dataArray)
The "magic" is in the view controller where you tell fetchNewData what to do when it's finished. Something like:
#IBAction func updateButton(_ sender: Any) {
fetchNewData() {(data)
// Save the data where the view controller can use it
self.tableArray = data
// Main queue for UI update
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Note that the closure is written in the view controller, so self is the view controller. This means no need to create a second (useless) controller inside the fetch.
I'm trying to create factory method on a class that automatically casts to the class it's on.
extension NSObject {
// how can I get the return type to be the current NSObject subclass
// instead of NSObject?
class func create() -> NSObject {
return self.init()
}
// example: create(type: NSArray.self)
class func create<T:NSObject>(type:T.Type) -> T {
return T()
}
}
Example two works, but gets NO advantage from being a class method:
let result = NSArray.create(type: NSArray.self)
But I'd love to be able to just call:
let result = NSArray.create()
without having to cast afterwards. Is there a way to do this in Swift?
You can use the class-level Self for this:
extension NSObject {
class func create() -> Self {
return self.init()
}
}
let array = NSArray.create()
But I don't really see why you would, since you might as well just add an initializer.
The accepted answer does the trick, thanks!
However, I needed this for a case where I wasn't calling the init directly. Instead, I had an object that was of type NSObject and needed a forced downcast
As #Hamish pointed out from this other SO answer, you can use the generic inference on a class method if you're another layer deep (a method called by a class method).
class func create() -> Self {
return createInner()
}
class func createInner<T>() -> T {
// upcasting to NSObject to show that we can downcast
let b = self.init() as NSObject
return b as! T
}
let array = NSArray.create() // gives me an NSArray
An Example with CoreData
I still can't figure out how to get the fetch part to compile, so I'm using an external function still.
import CoreData
// callers use
// try fetch(type: SomeMO.self, moc: moc)
func fetch<T:NSManagedObject>(type:T.Type, moc:NSManagedObjectContext) throws -> [T] {
return try T.fetch(moc: moc) as! [T]
}
extension NSManagedObject {
class func makeOne(moc:NSManagedObjectContext) -> Self {
return makeOneInner(moc: moc)
}
private class func makeOneInner<T>(moc:NSManagedObjectContext) -> T {
let name = "\(self)"
let retVal = NSEntityDescription.insertNewObject(forEntityName: name, into: moc)
return retVal as! T
}
class func fetch(moc:NSManagedObjectContext) throws -> [NSManagedObject] {
let fetchReq:NSFetchRequest<NSManagedObject> = self.fetchRequest() as! NSFetchRequest<NSManagedObject>
let retVal = try moc.fetch(fetchReq) as [NSManagedObject]
return retVal
}
}
I want to access stringEncoded in viewDidLoad but I cannot. How do I access it. Is it because it is not in main queue or is it about global variables staff?
I try new codes but still it doesn't work...
//Editted.
According to the code your provided, the var stringEncoded is defined into your jsonGetir function, while you want to access it from your viewDidLoad
So the solution is to put this variable in the class level, like this:
class YourClass{
var stringEncoded = "asomthing" //change the value or the type to whatever suites u
viewDidLoad{}
func jsonGetir(){}
}
Update
As you requested, a full working example:
import UIKit
class StViewController: UIViewController {
var stringEncoded = [String]()
func jsonGetir() {
let urls = NSURL(string: "http://gigayt.com/mackolik/deneme.php")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){
print(error)
}
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let x = jsonResult["kodlar"] as! String
self.stringEncoded = x.componentsSeparatedByString(",")
}
}
catch {
print(error)
}
dispatch_async(dispatch_get_main_queue()){
}
}
sessions.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<self.stringEncoded.count {
print(self.stringEncoded[i])
}
}
}
First declare the variable in class scope. If you want persist the that parameter then you can use inout parameter. Please refer to get more about inout parameter, hope you understand