So I ran into this tutorial, and followed it. I am just using it to store couple of boolenas.
Anyway I dont exactly understand how this script manages to store to iCloud since I have done nothing related to iCloud except simply allow it under capabilities. I have also tested it in couple scenarios and haven't had any issues. Can you think of times where this might be an issue?
http://nicemohawk.com/blog/2015/08/storing-key-value-data-in-icloud-with-swift/
If you are willing to look over my code to make sure I did it right please do! What is going on is their are six keys that are "HASpayedFOR1", "HASpayedFOR2" etc etc. And these store a bool on wether this character has been unlocked yet. Their are two storage spaces.
NSUserDefaults
And the NSUbiquitousKeyValueStore
NSUserDefaults is what usually stores everything and all the game values, NSUmbiquitousKeyValueStore is holding all the stuff I want on the "cloud". I have two main functions one that restores from the cloud, and another that saves to it. These are called when appropriate. Note that none of the functions allow it to go backwards. So for instance the restore code will only set things to true, and the save code will only set things to true.
Can you see any sort of crashing that could happen due to an unforeseen network error?
var iCloudKeyStore: NSUbiquitousKeyValueStore? = NSUbiquitousKeyValueStore()
func c_reportCloud()
{
if let iCloudKeyStore2 = iCloudKeyStore
{
for var i = 0; i < 7; i++
{
print("Icloud for " + String(i) + " :")
println(iCloudKeyStore2.boolForKey("HASpayedFOR" + String(i)))
}
}
}
func c_restoreCharecters()
{
iCloudKeyStore?.synchronize()
let defaults = NSUserDefaults.standardUserDefaults()
var current:[Bool] = []
for var i = 0; i < 7; i++
{
current.append(defaults.boolForKey("HASpayedFOR" + String(i)))
}
var cloud:[Bool] = []
if let iCloudKeyStore2 = iCloudKeyStore
{
for var i = 0; i < 7; i++
{
cloud.append(iCloudKeyStore2.boolForKey("HASpayedFOR" + String(i)))
}
}
var meme:[Bool] = []
for var i = 0; i < 7; i++
{
//If we dont have it and the cloud does then get it from the cloud!
if (current[i] == false)
{
if (cloud[i] == true)
{
meme.append(true)
defaults.setBool(true, forKey: "HASpayedFOR" + String(i))
}
else
{
meme.append(false)
}
}
else
{
meme.append(false)
}
}
//Force the opening of them
if let it = spriteKit.scene as? Menu
{
it.open(meme)
}
// iCloudKeyStore?.setString(textField.text, forKey: iCloudTextKey)
/*for var i = 0; i < 7; i++
{
print("Has unlocked charecter: " + String(i))
println(defaults.boolForKey("HASpayedFOR" + String(i)))
}*/
}
func c_savePlayers()
{
iCloudKeyStore?.synchronize()
let defaults = NSUserDefaults.standardUserDefaults()
var current:[Bool] = []
for var i = 0; i < 7; i++
{
current.append(defaults.boolForKey("HASpayedFOR" + String(i)))
}
var cloud:[Bool] = []
if let iCloudKeyStore2 = iCloudKeyStore
{
for var i = 0; i < 7; i++
{
cloud.append(iCloudKeyStore2.boolForKey("HASpayedFOR" + String(i)))
}
}
if let iCloudKeyStore2 = iCloudKeyStore
{
for var i = 0; i < 7; i++
{
//If we have something the cloud doesnt
if (current[i] == true)
{
if (cloud[i] == false)
{
iCloudKeyStore2.setBool(true, forKey: "HASpayedFOR" + String(i))
}
}
}
}
iCloudKeyStore?.synchronize()
}
Thanks much!
Related
I have a game that has 9 different levels which have their own highscore values for coins collected and special coins collected. I want to make a "Bank" that can store those values and add them up to be able to 'purchase' the unlock for some levels. I'm not sure if I implemented my scoring system in a way that won't allow this or if I'm missing something simple. Any insight is greatly appreciated!
import Foundation
struct ScoreManager {
static func getCurrentScore(for levelKey: String) -> [String:Int] {
if let existingData = UserDefaults.standard.dictionary(forKey: levelKey) as? [String:Int] {
return existingData
} else {
return [GameConstants.StringConstants.scoreScoreKey:0, GameConstants.StringConstants.scoreStarsKey:0, GameConstants.StringConstants.scoreCoinsKey:0]
}
}
static func updateScore(for levelKey: String, and score: [String:Int]) {
UserDefaults.standard.set(score, forKey: levelKey)
UserDefaults.standard.synchronize()
}
static func compare(scores: [[String:Int]], in levelKey: String) {
var newHighscore = false
let currentScore = getCurrentScore(for: levelKey)
var maxScore = currentScore[GameConstants.StringConstants.scoreScoreKey]!
var maxStars = currentScore[GameConstants.StringConstants.scoreStarsKey]!
var maxCoins = currentScore[GameConstants.StringConstants.scoreCoinsKey]!
for score in scores {
if score[GameConstants.StringConstants.scoreScoreKey]! > maxScore {
maxScore = score[GameConstants.StringConstants.scoreScoreKey]!
newHighscore = true
}
if score[GameConstants.StringConstants.scoreStarsKey]! > maxStars {
maxStars = score[GameConstants.StringConstants.scoreStarsKey]!
newHighscore = true
}
if score[GameConstants.StringConstants.scoreCoinsKey]! > maxCoins {
maxCoins = score[GameConstants.StringConstants.scoreCoinsKey]!
newHighscore = true
}
}
if newHighscore {
let newScore = [GameConstants.StringConstants.scoreScoreKey: maxScore, GameConstants.StringConstants.scoreStarsKey: maxStars, GameConstants.StringConstants.scoreCoinsKey: maxCoins]
updateScore(for: levelKey, and: newScore)
}
}
And this is called in the GameScene after you finish the level..
func finishGame() {
gameState = .finished
var stars = 0
let percentage = CGFloat(coins)/100.0
if percentage >= 0.8 {
stars = 3
} else if percentage >= 0.4 {
stars = 2
} else if coins >= 1 {
stars = 1
}
let scores = [
GameConstants.StringConstants.scoreScoreKey: coins,
GameConstants.StringConstants.scoreStarsKey: stars,
GameConstants.StringConstants.scoreCoinsKey: superCoins
]
ScoreManager.compare(scores: [scores], in: levelKey)
createAndShowPopup(type: 1, title: GameConstants.StringConstants.completedKey)
if level < 9 {
let nextLevelKey = "Level_\(world)-\(level+1)_Unlocked"
UserDefaults.standard.set(true, forKey: nextLevelKey)
UserDefaults.standard.synchronize()
}
}
Ignore the stars, I used these to show basically how well you did on the level. I'll gladly provide more code snippets if needed too. Thank you all again!
I have a generator that generates a circle(SKShapeNode) every 2 seconds from the y-axis. (Moves up). I would like to know if it is possible to populate this generator uniformly(every x number of points.
Here is my code for said generator:
class CircleGen: SKShapeNode {
var circles = [WhiteCircles]()
var circleTracker = [WhiteCircles]()
var generationTimer: NSTimer!
var circ: WhiteCircles!
func generateCircles() {
for var i = 0; i < 4; i++ {
var scale: CGFloat
let rand = arc4random_uniform(10)
if rand == 0 {
scale = -15.0
} else {
scale = 15.0
}
let circle = WhiteCircles()
circle.position.x = lineGen.position.x
circle.physicsBody?.affectedByGravity = false
circle.position.y = 35
self.circles.append(circle)
circleTracker.append(circle)
foregroundNode.addChild(circle)
}
}
func startGeneratingEvery(seconds: NSTimeInterval) {
let callGenerateAction = SKAction.runBlock { () -> Void in
self.generateCircles()
}
let waitOneSecond = SKAction.waitForDuration(seconds)
let sequenceAction = SKAction.sequence([callGenerateAction, waitOneSecond])
let circleGenerateAction = SKAction.repeatActionForever(sequenceAction)
self.runAction(circleGenerateAction, withKey: "generateCircles")
}
}
Will post more code if necessary. Thank you in advance.
I'm building a today extension for iOS and I'm confused why this function keeps on using more and more memory.
See the code below:
func loadActivePlayer() {
kodi.getActivePlayer {
(response) in
let result = JSON(response)
let activePlayer = ActivePlayer()
if let playerid = result["result"][0]["playerid"].int {
activePlayer.playerid = playerid
}
if let type = result["result"][0]["type"].string {
activePlayer.type = type
}
if(activePlayer.isEmpty()) {
self.loadActivePlayer()
}
else {
self.enablePlaybackButtons(true)
self.activePlayer = activePlayer
self.kodi.getItem(activePlayer.playerid) {
(response) in
var result = JSON(response)
var item = Item()
if let id = result["result"]["item"]["id"].int {
item.id = id
}
if let type = result["result"]["item"]["type"].string {
item.type = type
}
if let label = result["result"]["item"]["label"].string {
item.label = label
}
if let title = result["result"]["item"]["title"].string {
item.title = title
}
if let season = result["result"]["item"]["season"].int {
item.season = season
}
if let episode = result["result"]["item"]["episode"].int {
item.episode = episode
}
if let showtitle = result["result"]["item"]["showtitle"].string {
item.showtitle = showtitle
}
dispatch_async(dispatch_get_main_queue()) {
self.itemTitleLabel.text = item.title
self.itemShowTitleLabel.text = item.showtitle
self.itemSeasonEpisodeLabel.text = "S\(item.season)E\(item.episode)"
}
self.loadActivePlayer()
}
}
}
When it goes in the if(activePlayer.isEmpty())
the memory usage increases and increases and increases, quite irritating.
But when it goes into the else, the memory usage stays almost the same, it does not increase significantly.
Can anyone help me understand why this is happening?
Thanx in advance!
I'm trying to recreate the feed on the facebook app and I'm having trouble inserting the newly downloaded cells. The cells are being inserted, but like this SO question: Trying to insert new items into UICollectionView but getting the same cells over and over , it keeps inserting the same results, the initial first 10 posts in this case.
What i've done is this:
jsonRequest.getPosts(hashKey!, request: populateFeedRequest) { (succeeded:Bool, msg:String, results:[AnyObject]) -> () in
var resultsAreDone:Bool!
if succeeded {
if (results.isEmpty == false) {
++self.lastFeedPostId
resultsAreDone = true
} else {
resultsAreDone = false
}
} else {
println("you weren't able to download the posts")
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if (resultsAreDone == true) {
var updatedArray:Array = [Dictionary<String, AnyObject>]()
self.postsView.performBatchUpdates({
let resultsSize = self.feedArray.count
var size = 0
if let newArray = results as? [Dictionary<String, AnyObject>] {
for n in newArray {
self.feedArray.append(n)
}
size = resultsSize + newArray.count
}
var arrayWithIndexPaths = NSMutableArray()
var i = 0
for (i = resultsSize; i < size; i++) {
arrayWithIndexPaths.addObject(NSIndexPath(forRow: i, inSection: 0))
}
self.postsView.insertItemsAtIndexPaths(arrayWithIndexPaths as [AnyObject])
},
completion: nil)
}
else {
println("it didn't work")
}
})
}
the lastFeedPostId is used to grab the next 10 results from the server.
Also a sidenote: i'm doing this in the "cellforItemAtIndexPath" function. I'm using the current Indexpath to determine if I should pull the next 10 posts from the server or not. Is this correct, or is there a better way to do this?
Thank you so much!!
I am loading Audio assets via AVAssets. I want to figure out how many channels (mono or stereo basically) are in the asset. What is the best way to do this?
This appears to be what I am looking for.
AVAssetTrack* songTrack = [mAssetToLoad.tracks objectAtIndex:0];
NSArray* formatDesc = songTrack.formatDescriptions;
for(unsigned int i = 0; i < [formatDesc count]; ++i) {
CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i];
const AudioStreamBasicDescription* bobTheDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item);
if(bobTheDesc && bobTheDesc->mChannelsPerFrame == 1) {
mIsMono = true;
}
}
Swift 5 implementation of TurqMage's answer
//
// AVAssetTrack+IsStereo.swift
//
import AVFoundation
extension AVAssetTrack {
var isStereo: Bool {
for item in (formatDescriptions as? [CMAudioFormatDescription]) ?? [] {
let basic = CMAudioFormatDescriptionGetStreamBasicDescription(item)
let numberOfChannels = basic?.pointee.mChannelsPerFrame ?? 0
if numberOfChannels == 2 {
return true
}
}
return false
}
}