Saving and Reading information from a Sprites UserData - swift

I'm trying to have a value be saved onto a sprite's user data and be able to read it off.
Heres what I have, for some reason the value never gets saved and stays as nil.
import SpriteKit
let frame = SKScene()
func createSprite() {
let sprite = SKSpriteNode()
sprite.userData?.setValue("100", forKeyPath: "key")
frame.addChild(sprite)
}
createSprite()
for child in frame.children where child is SKSpriteNode {
print(child.userData?.valueForKey("key") as? String)
//prints the value saved in the childs user data
if child.userData?.valueForKey("key") as? String == "100" {
print("It was a Success!")
}
if child.userData?.valueForKey("key") as? String == nil {
print("There was a problem :(")
}
}

I believe your issue is that the userData starts out as nil. Try this...
func createSprite() {
let sprite = SKSpriteNode()
//init NSMutableDictionary
sprite.userData = NSMutableDictionary()
sprite.userData?.setValue("100", forKeyPath: "key")
frame.addChild(sprite)
}
Hopefully that helps.

Related

Child references in Firebase being changed for unknown reason

I am currently working on a project that involves uploading cards to a database. I am using Swift 4 and using google's firebase as the database. I have made the code function for placing cards from your hand onto the playing area, but when they are moved from one tile on the playing area to another tile it creates a duplicate of the card instead of moving the card that is already there. From closer inspection I have found that this is because the snapshot.ref in the retrieveUpdates method does not retrieve the correct reference value of the card that is already in the database. That causes it to call the code to create a new card.
My question is thus: why are the values of the snapshot.ref changing between when they passed by one used and received by another?
//Sends a card and location to the database when a card is moved
func updateDatabase(playingCard: PlayingCard) {
let cardUpdate = Database.database().reference().child("Updates")
//This takes the location that the sender dropped the card.
if let location = game.location(from: playingCard.card) {
if case .battlefield(let field, let stack) = location {
//If the card is already in the database it should keep its old ID but if it is new it should be assigned a new ID
let reference = playingCard.databaseRef ?? cardUpdate.childByAutoId()
let updateDictionary = ["Sender": String(playerNumber),"Card": playingCard.card.name, "Field": String(field), "Stack": String(stack)]
if playingCard.databaseRef == nil {
playingCard.databaseRef = reference
reference.setValue(updateDictionary)
}
else {
reference.updateChildValues(updateDictionary)
}
}
}
}
func retrieveUpdates() {
let cardUpdate = Database.database().reference().child("Updates")
cardUpdate.observe(.childAdded) { (snapshot) in
if snapshot.childrenCount != 0 {
self.processUpdate(snapshot: snapshot)
}
}
cardUpdate.observe(.childChanged, with: { (snapshot) in
self.processUpdate(snapshot: snapshot)
})
}
func processUpdate(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! Dictionary<String,String>
let cardName = snapshotValue["Card"]!
let sender = Int(snapshotValue["Sender"]!)
let stack = Int(snapshotValue["Stack"]!)
let relativeField = snapshotValue["Field"]!
if sender != self.playerNumber {
let fieldNumber = (4 + sender! - self.playerNumber + Int(relativeField)!) % 4
var foundCard = false
for playingCard in self.gameGraphics.cards.reversed() {
if let databaseRef = playingCard.databaseRef, databaseRef == snapshot.ref {
foundCard = true
self.currentPlayingCard = CurrentPlayingCard(playingCard: playingCard, startPosition: playingCard.position, touchPoint: playingCard.position, location: Location.dataExtract())
}
}
if foundCard == false {
let card = self.gameGraphics.addFromDatabase(name: cardName, field: fieldNumber, stack: stack!, scene: self)
card.databaseRef = snapshot.ref
self.currentPlayingCard = CurrentPlayingCard(playingCard: card, startPosition: card.position, touchPoint: card.position, location: Location.dataExtract())
}
self.touchUp(atPoint: self.gameGraphics.allBattlefields[fieldNumber][stack!].position)
}
}
Any help would be greatly appreciated. This is one of my first programs written in swift so my coding practices are still not very good.
Here is the scene did load.
override func sceneDidLoad() {
super.sceneDidLoad()
anchorPoint = CGPoint(x: 0, y: 1)
FirebaseApp.configure()
let login = loginInfo()
Auth.auth().signIn(withEmail: login.username, password: login.password) { (user, error) in
if error != nil {
print(error!)
} else {
print("login Successful")
}
}
retrieveUpdates()
}

Showing just one image at the time from database (Swift and Firebase)

First of all, I am new in this, so please do not make fun of me :)
Basically, I am trying to show and Image of a product but if the client refuses the product this item will not appear on his account. That is why I am creating another table Rejected (setAcceptedOrRejected) where I put the ID of the product and the Id of the client so I wont see the item he rejected before.
What I tried here it was to get the List (Good) with all the items and the (Bad) with the rejected items. Then compare it to display the picture of the item again.
My problem is that I want to show only 1 picture at the time, if the client refuses then it will show the next one and so on but it wont show that picture again.
I hope you can really help me with this one.
Thank you
func updateImage() {
createListProductsBad ()
var badnot = ""
for bad2 in listProductsBad{
badnot = bad2
}
Database.database().reference().child("Products").child(bad2)queryOrderedByKey().observe(.childAdded, with: { snapshot in
let userInfo = snapshot.value as! NSDictionary
let storageRef = Storage.storage().reference(forURL: profileUrl)
storageRef.downloadURL(completion: { (url, error) in
do {
let data = try Data(contentsOf: url!)
let image = UIImage(data: data as Data)
self.productPhoto.image = image
}
catch _ {
print("error")
}
})
})
}
func setAcceptedOrRejected() {
let notThankyou = [ "ProductID": ProductId,
"UserID": userUID
] as [String : Any]
self.storyboard?.instantiateViewController(withIdentifier: "Home")
self.refProducts.child("Rejected").childByAutoId().setValue(notThankyou)
}
func createListProductsGood () {
Database.database().reference().child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
if !snapshot.exists() { return }
let userInfo = snapshot.value as! NSDictionary
let goodID = String(snapshot.key)
for prod in self.listProductsBad{
if (prod == goodID){
print("Not good **********************")
}else{
if (goodID != "" ){
self.listProductsGood.append(prod)
}
}
}
})
}
func createListProductsBad () {
Database.database().reference().child("Rejected").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let userInfo = snapshot.value as! NSDictionary
let currentID = userInfo["UserID"] as! String
let badProduct = userInfo["ProductID"] as! String
if (self.userUID == currentID ){
self.listProductsBad.append(badProduct)
}
})
}
}
//These can also be swift's dictionaries, [String: AnyObject] or possibility arrays if done correctly. All depends on your style of programming - I prefer NSDictionaries just because.
let availableKeys: NSMutableDictionary = [:]
let rejectedKeys: NSMutableDictionary = [:]
//Might be a better way for you. Depends on what you are looking for.
func sortItems2() -> NSMutableDictionary{
for rejKey in rejectedKeys.allKeys{
//Removes if the rejected key is found in the available ones
availableKeys.remove(rejKey)
}
return availableKeys
}

Retrieve Firebase value and set SKLabelNode text

This is probably a very simple question.
This is the database:
I want to retrieve this "New" value which is 1 and put on a SKLabelNode.
The problem is that this code is printing 10 correctly but setting nil to SKLabelNode on the emulator.
dbRef.observeSingleEvent(of: .value) { (snap: FIRDataSnapshot) in
if !snap.exists() {
return
}else{
print(snap.childSnapshot(forPath: "New").value!)
self.label.text = snap.childSnapshot(forPath: "New").value as! String?
}
}

Reloading NSTableView after downloading data

I'm writing an app that will help me update my database on parse and I'm trying to load objects into an nstableview.
For whatever reason, I'm returning nil when I make the call in my viewcontroller so I moved the data into my appdelegate to get the objects.
let storyBoard = NSStoryboard(name: "Main", bundle: nil)
let myVC = storyBoard.instantiateControllerWithIdentifier("teacher") as! ViewController
var teachers = [Teacher]()
let query = PFQuery(className: "TeacherList")
query.findObjectsInBackgroundWithBlock {
objects, error in
if let objects = objects {
for object in objects {
let teacher = Teacher()
teacher.name = object["name"] as! String
teacher.email = object["email"] as! String
teacher.subjectsTaught = object["subjectsTaught"] as! [String: String]
teacher.category = object["category"] as! String
teacher.uniqueID = object.objectId!
teachers.append(teacher)
}
}
print(teachers)
myVC.teacherList = teachers
}
As you see, I pass this along to my VC. So I understand that I need to reload the data as viewDidLoad() will be called before the data has been downloaded. I've tried putting tableView.reloadData() in didSet for teacherList and just in case that's set before the viewloads, I even throw it in viewdidload.
var teacherList: [Teacher]? {
didSet {
print("got set")
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.setDelegate(self)
tableView.setDataSource(self)
tableView.reloadData()
}
However, no matter what, my tableview is nil for anything that happens after viewdidload. I've also tried optional channing in my getSet.
I should also say that I'm brand new to OSX programming as I've done most of my programming for iOS.
I can put the query in my VC but whenever I run the query, I return nil.
PFQuery works asynchronously, the data is returned much later - in terms of computer speed – after viewDidLoad exits.
Reload the table view in the block right after the array has been populated on the main thread.
var teachers = [Teacher]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.setDelegate(self)
tableView.setDataSource(self)
let query = PFQuery(className: "TeacherList")
query.findObjectsInBackgroundWithBlock { [unowned self] (objects, error) in
if let objects = objects {
for object in objects {
let teacher = Teacher()
teacher.name = object["name"] as! String
teacher.email = object["email"] as! String
teacher.subjectsTaught = object["subjectsTaught"] as! [String: String]
teacher.category = object["category"] as! String
teacher.uniqueID = object.objectId!
self.teachers.append(teacher)
}
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
print(self.teachers)
}
}

Displaying Artwork for .MP3 file

I am trying to currently display the album artwork for a locally stored .MP3 track in an ImageView. Does anyone know how to fetch this artwork in Swift in order to accomplish this?
I have found this solution (iOS AVFoundation: How do I fetch artwork from an mp3 file?) but the code is written in Objective C. I simply want to grab the image embedded in my MP3 and display it in my ImageView.
I've looked at the API documentation for the MPMediaItemArtwork and found an example that also accomplishes what I am trying to accomplish in Objective C as well here(http://www.codeitive.com/0zHjkUjUWX/not-able-to-get-the-uiimage-from-mpmediaitempropertyartwork.html) but cannot come up with a solution. My code is as follows:
import UIKit
import AVFoundation
import MediaPlayer
class ViewController: UIViewController {
let audioPath:NSURL! = NSBundle.mainBundle().URLForResource("SippinOnFire", withExtension: "mp3")
#IBOutlet var artistImage: UIImageView!
#IBOutlet var trackLabel: UILabel!
#IBOutlet var artistLabel: UILabel!
#IBOutlet var sliderValue: UISlider!
var player:AVAudioPlayer = AVAudioPlayer()
#IBAction func play(sender: AnyObject) {
let audioInfo = MPNowPlayingInfoCenter.defaultCenter()
println(audioInfo)
player.play()
//println("Playing \(audioPath)")
let playerItem = AVPlayerItem(URL: audioPath)
let metadataList = playerItem.asset.metadata as! [AVMetadataItem]
for item in metadataList {
if let stringValue = item.value {
println(item.commonKey)
if item.commonKey == "title" {
trackLabel.text = stringValue as? String
}
if item.commonKey == "artist" {
artistLabel.text = stringValue as? String
}
if item.commonKey == "artwork" {
if let audioImage = UIImage(data: item.value as! NSData) {
let audioArtwork = MPMediaItemArtwork(image: audioImage)
println(audioImage.description)
}
}
}
}
}
#IBAction func pause(sender: AnyObject) {
player.pause()
}
#IBAction func stop(sender: AnyObject) {
player.stop()
player.currentTime = 0;
}
#IBAction func sliderChanged(sender: AnyObject) {
player.volume = sliderValue.value
}
override func viewDidLoad() {
super.viewDidLoad()
var error:NSError? = nil
player = AVAudioPlayer(contentsOfURL: audioPath!, error: &error)
player.volume = 0.5;
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is a screen shot of my sample .mp3 file. As you can see there is indeed album artwork that is both visible in the "get info" section of Finder. I've also opened the .mp3 in my iTunes to make sure and have confirmed there is artwork in the "get info" section of it there as well as under the "artwork" tab.
However, when trying to use the commonKey to assign the image to my imageView I find that there is no commonKey for "artwork".
Thanks
Change your snippet of code into this (I already tested it):
I added println lines commented in places of interest, Feel free to uncomment in order to see what is happening.
for item in metadataList {
if item.commonKey == nil{
continue
}
if let key = item.commonKey, let value = item.value {
//println(key)
//println(value)
if key == "title" {
trackLabel.text = value as? String
}
if key == "artist" {
artistLabel.text = value as? String
}
if key == "artwork" {
if let audioImage = UIImage(data: value as! NSData) {
//println(audioImage.description)
artistImage.image = audioImage
}
}
}
}
UPDATE: A bit of clean up of this code
for item in metadataList {
guard let key = item.commonKey, let value = item.value else{
continue
}
switch key {
case "title" : trackLabel.text = value as? String
case "artist": artistLabel.text = value as? String
case "artwork" where value is NSData : artistImage.image = UIImage(data: value as! NSData)
default:
continue
}
}
UPDATE: For Swift 4
for item in metadataList {
guard let key = item.commonKey?.rawValue, let value = item.value else{
continue
}
switch key {
case "title" : trackLabel.text = value as? String
case "artist": artistLabel.text = value as? String
case "artwork" where value is Data : artistImage.image = UIImage(data: value as! Data)
default:
continue
}
}
edit/update Swift 4 or later:
import MediaPlayer
var nowPlayingInfo: [String: Any] = [:]
let playerItem = AVPlayerItem(url: url)
let metadataList = playerItem.asset.metadata
for item in metadataList {
switch item.commonKey {
case .commonKeyTitle?:
nowPlayingInfo[MPMediaItemPropertyTitle] = item.stringValue ?? ""
case .commonKeyType?:
nowPlayingInfo[MPMediaItemPropertyGenre] = item.stringValue ?? ""
case .commonKeyAlbumName?:
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = item.stringValue ?? ""
case .commonKeyArtist?:
nowPlayingInfo[MPMediaItemPropertyArtist] = item.stringValue ?? ""
case .commonKeyArtwork?:
if let data = item.dataValue,
let image = UIImage(data: data) {
nowPlayingInfo[MPMediaItemPropertyArtwork] = MPMediaItemArtwork(boundsSize: image.size) { _ in image }
}
case .none: break
default: break
}
}
let audioInfo = MPNowPlayingInfoCenter.default()
audioInfo.nowPlayingInfo = nowPlayingInfo
Note: You will have to invoke beginReceivingRemoteControlEvents() otherwise it will not work on the actual device. You will also need to set your app Background Modes (Audio and AirPlay) and set your AVAudioSession category to AVAudioSessionCategoryPlayback and set it active:
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.mixWithOthers, .allowAirPlay])
print("Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("Session is Active")
} catch {
print(error)
}
Try this:
It appears that sometimes iOS 8 returns nil at first attempt of obtaining this info:
if let audioCenter = MPNowPlayingInfoCenter.defaultCenter(){
if let audioInfo = audioCenter.nowPlayingInfo{
if let artwork = audioInfo[MPMediaItemPropertyArtwork] as? MPMediaItemArtwork
{
var image: UIImage? = artwork.imageWithSize(artwork.bounds.size)
if image == nil {
image = artwork.imageWithSize(artwork.bounds.size);
}
if image != nil{
println("image loaded")
}
}
}
}