iOS baresip with SIP Calling - ios

I am trying to develop an application which provides Audio and Video calling, Now I am following baresip library for the same.
and I wrote following code on button Click :
#IBAction func btnCallClick(_ sender: Any) {
guard libre_init() == 0 else { return }
// Initialize dynamic modules.
mod_init()
// Make configure file.
if let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
conf_path_set(path)
}
guard conf_configure() == 0 else { return }
// Initialize the SIP stack.
guard baresip_init(conf_config(), 0) == 0 else { return }
guard ua_init("SIP", 1, 1, 1, 0) == 0 else { return }
// Load modules.
guard conf_modules() == 0 else { return }
let addr = "sip:101#xxx.xxx.com:port;auth_pass=aaaa;transport=udp;answermode=auto"
// Start user agent.
guard ua_alloc(&agent, addr) == 0 else { return }
// Make an outgoing call.
guard ua_connect(agent, nil, nil, "sip:100#xxx.xxx.com", VIDMODE_OFF) == 0 else { return }
// Start the main loop.
re_main(nil)
}
Now, I am getting a call from one device to another device but it hangs my view, Why it's hanging view? I spent lots of time, anyone can help me?

Related

Synchronization issue in synchronized thread

Sorry ahead of time for posting so much code. I seem to have a synchronization issue causing my app to crash. I put all of the code in a sync thread so I'm not sure how this is possible. It crashes in two different places depending on how frequently the function is being called, but I suspect the source of the problem is the same. Please let me know if you have any advice.
Code
var curCollisionChecks = 0
func okToCheckForCollision() -> Bool {
print(curCollisionChecks)
if (curCollisionChecks < 4) {
let collisionQueue = DispatchQueue(label: "collisionQueue")
collisionQueue.sync {
curCollisionChecks += 1
}
return true
}
logText(text: "WARNING: Checking too many collisions")
logText(text: "WARNING: Some may be ignored")
return false
}
func checkColision(object: UIView) {
if (!viewThatRequireChecking.contains((objectToFO[object]?.name)!)){
return
}
let collisionQueue = DispatchQueue(label: "collisionQueue")
collisionQueue.sync {
if (!okToCheckForCollision()) {
return
}
for view in objectToFO.keys {
if (!viewThatRequireChecking.contains((objectToFO[view]?.name)!)){
continue
}
let s0 = [object,view]
let s1 = [view,object]
if (view.frame.intersects(object.frame)) {
if (view == object) {
continue
}
var shouldContinue = false
for combo in collisionsStatus {
if (combo == s0) {
shouldContinue = true
break
}
if (combo == s1) {
shouldContinue = true
break
}
}
if (shouldContinue) {
continue
}
collisionsStatus.append(s0) // SOMETIMES BREAKS HERE
collisionsStatus.append(s1)
let fo1 = objectToFO[view]
let fo2 = objectToFO[object]
if (viewToCollisionParents.keys.contains(view)) {
let script1 = ScriptObjects[(fo1?.name)!]
for curParent in viewToCollisionParents[view]! {
let deplexed = deplex(snap: curParent, object: view)
if ((deplexed[0] as! String) == "Myself") {
if (fo2?.name == fo1?.name) {
run(sender: view, curScript: script1!, parent: curParent)
}
}
else {
if (fo2?.name == (deplexed[0] as! String)) {
run(sender: view, curScript: script1!, parent: curParent)
}
}
}
}
if (viewToCollisionParents.keys.contains(object)) {
let script2 = ScriptObjects[(fo2?.name)!]
for curParent in viewToCollisionParents[object]! {
let deplexed = deplex(snap: curParent, object: object)
if ((deplexed[0] as! String) == "Myself") {
if (fo2?.name == fo1?.name) {
run(sender: object, curScript: script2!, parent: curParent)
}
}
else {
if (fo1?.name == (deplexed[0] as! String)) {
run(sender: object, curScript: script2!, parent: curParent)
}
}
}
}
}
else {
var newCollisionsStatus = [[UIView]]()
// SOMETIMES BREAKS HERE
for combo in collisionsStatus {
if (combo != s0 && combo != s1) {
newCollisionsStatus.append(combo)
}
}
collisionsStatus = newCollisionsStatus
}
}
if (curCollisionChecks > 0) {
curCollisionChecks -= 1
}
}
}
Types of error messages
fatal error: Index out of range
fatal error: UnsafeMutablePointer.deinitialize with negative count
One clear issue is your misuse of DispatchQueue and sync. You create a new queue each time so it does nothing to protect access to the properties. Create a single queue as an instance property and use it each time. This will ensure only one thread at a time can access the code in the sync block.

MPMusicPlayerController.shuffleMode cannot be set

Hello I have a question about the MPMusicPlayerController in Swift. I am currently working on a Music App were I want to shuffle music by songs. So when the App Starts it basically sets the Playback Queue and then the Shuffle Mode. I can successfully set the queue (and play the songs) but I get an error when I set the Shuffle Mode:
musicPlayer.musicPlayer.shuffleMode = .songs
ERROR:
2018-07-03 15:01:36.450977+0200 Hitbeat[29053:8378883] [SDKPlayback] -[MPMusicPlayerController setShuffleMode:2] completed error: Error Domain=MPCPlayerRequestErrorDomain Code=1 "No commands provided." UserInfo={NSDebugDescription=No commands provided.}
What does that mean?
I have the idea that it may be because the queue is not set completely when setting the shuffleMode but I am not sure and it would not make any sense that one would have to set a song queue first in order to set the mode in which order songs to play. Maybe something else is the problem?
Also everything takes place on the Main Thread. (MPMusicPlayerController always has to be called in the Main Thread)
Thanks a lot I hope you guys can help me.
here are some code snippets:
MusicPlayerManager.swift
import os.log
import MediaPlayer
import NotificationCenter
class MusicPlayerManager {
let musicPlayer: MPMusicPlayerController
lazy var musicPickerAndAdder = MusicPickerAndAdder()
init() {
// Instantiate a new music player
musicPlayer = MPMusicPlayerApplicationController.applicationQueuePlayer
// Add a playback queue containing all songs on the device
switch MPMediaLibrary.authorizationStatus() {
case .authorized:
let catalogSongStoreID: String = ""
let catalogQueueDescriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: [catalogSongStoreID])
musicPlayer.setQueue(with: catalogQueueDescriptor)
default:
break
}
Timer.scheduledTimer(withTimeInterval: 15, repeats: false) {_ in
print("shuffle mode setter")
self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs
}
}
func updateOnlineMusicQueue() {
var musicPickerIds = [String]()
DispatchQueue.global(qos: .userInitiated).sync {
musicPickerIds = musicPickerAndAdder.ids
}
if !musicPickerIds.isEmpty{
musicPlayer.setQueue(with: musicPickerIds)
}else {
updateOfflineMusicQueue()
}
musicPlayer.pause()
}
func play() {
if musicPlayer.playbackState == .playing {
musicPlayer.pause()
musicPlayer.skipToBeginning()
}
if !musicPlayer.isPreparedToPlay {
musicPlayer.prepareToPlay { (error) in
if error == nil {
self.musicPlayer.play()
self.startSongMasterTimer()
}
}
}else {
musicPlayer.play()
startSongMasterTimer()
}
}
func pauseAndSkip() {
// if self.musicPlayer.shuffleMode.rawValue != 2 { // does not work here would work on pause and skip
// self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs
// }
//print("shuffler \(self.musicPlayer.shuffleMode.rawValue)")
//print("At \(musicPlayer.currentPlaybackTime) of \((musicPlayer.nowPlayingItem?.playbackDuration!)")
musicPlayer.pause()
//if musicPlayer.nowPlayingItem != nil {
musicPlayer.skipToNextItem()
//}
musicPlayer.prepareToPlay { (error) in
if error == nil {
self.musicPlayer.pause()
}
}
}
func currentSongInfo() -> SongInfo {
let songTitle = musicPlayer.nowPlayingItem?.title?.replacingOccurrences(of: "-", with: " ") ?? "" // To guarantee there is only one - between Song and Artist
let artistName = musicPlayer.nowPlayingItem?.artist?.replacingOccurrences(of: "-", with: " ") ?? ""
let songInfo = SongInfo(title: songTitle, artist: artistName)
return songInfo
}
func addSongToLibrary() {
//print("Id of Item to Add: \(musicPlayer.nowPlayingItem?.playbackStoreID)")
if musicPlayer.nowPlayingItem != nil {
musicPickerAndAdder.addResourceToUserMusicLibrary(resourceId: (musicPlayer.nowPlayingItem?.playbackStoreID)!)
}
//ToDo add to myHitbeat Playlist
}
}
class SongInfo {
let title: String
let artist: String
init(title:String,artist:String) {
self.title = title
self.artist = artist
}
}
MusicPickerAndAdder.swift
import Foundation
class MusicPickerAndAdder {
lazy var authorizationManager: AuthorizationManager = {
return AuthorizationManager(appleMusicManager: self.appleMusicManager)
}()
var appleMusicManager = AppleMusicManager()
private var idsArraySize = 100
static var idCategoriesStakes = ["Chart_Ids" : 0.10,
"Recently_Played_Ids" : 0.10,
"Experiment_Ids" : 0.30,
"Recommendations_Ids" : 0.50,] // Addition of all Values must be 1 (100%)
private var chartIds: [String] {
var chartsIds = [String]()
let chartsIdsGroup = DispatchGroup()
chartsIdsGroup.enter()
let limit = Int(Double(idsArraySize) * MusicPickerAndAdder.idCategoriesStakes["Recently_Played_Ids"]!)
appleMusicManager.performAppleMusicGetChartSongs(regionCode: Locale.current.regionCode?.lowercased() ?? "us", limit: limit) { (storeIds, error) in
if error != nil {
print("There was an Error getting Charts")
chartsIdsGroup.leave()
return
}
chartsIds = storeIds
chartsIdsGroup.leave()
}
chartsIdsGroup.wait()
print("Charts sucessfully fetched")
return chartsIds
}
private var recentlyPlayedIds: [String] {
var recentIds = [String]()
let recentIdsGroup = DispatchGroup()
recentIdsGroup.enter()
let limit = Int(Double(idsArraySize) * MusicPickerAndAdder.idCategoriesStakes["Recently_Played_Ids"]!)
appleMusicManager.performAppleMusicGetRecentlyPlayed(userToken: authorizationManager.userToken, limit: limit) {
(storeIds, error) in
if error != nil {
print("There was an Error getting Recently Played")
recentIdsGroup.leave()
return
}
recentIds = storeIds
recentIdsGroup.leave()
}
recentIdsGroup.wait()
print("Recently Played sucessfully fetched: \(recentIds)")
return recentIds
}
private var experimentIds: [String] {
return ["pl.u-XkD04oZIY0Kxrl"]
}
private var recommendationsIds: [String] {
return [String]()
}
// Never request Ids in Main (UI) Thread
var ids: [String] {
var ids = [String]()
ids += recentlyPlayedIds
ids += chartIds
ids += experimentIds
ids += recommendationsIds
print("Store Ids for Songs \(ids)")
return ids.shuffled() // shuffles list of items
}
init() {
requestAppleMusicAuthorization()
}
//MARK: Private Methods
private func requestAppleMusicAuthorization() {
UserDefaults.standard.register(defaults: ["tutorial": true])
if !UserDefaults.standard.bool(forKey: "tutorial") {
authorizationManager.requestCloudServiceAuthorization()
authorizationManager.requestMediaLibraryAuthorization()
}
}
}
extension MusicPickerAndAdder { // to Add Songs
func addResourceToUserMusicLibrary(resourceId: String) {
appleMusicManager.performAddResourceToLibrary(resourceId: resourceId, userToken: authorizationManager.userToken)
}
}
extension MutableCollection {
/// Shuffles the contents of this collection.
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
// Change `Int` in the next line to `IndexDistance` in < Swift 4.1
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
extension Sequence {
/// Returns an array with the contents of this sequence, shuffled.
func shuffled() -> [Element] {
var result = Array(self)
result.shuffle()
return result
}
}
PS: MusicPickerAndAdder may look a little messy though I don't think the problem lies there! What it basically does is fetching some data from the Apple Music API which works fine, and adding Songs to the User Library which works too.
Okay after trying everything out possible I came up with two solutions that work for me. Weirdly I found out that a freeze of the interface only occurs when no song has played so far. If a song is currently playing or even if a song has played and was paused afterwards there is no ui freeze. So I came up with this function:
private func setShuffleMode() { // does work though startup and restarting takes longer
musicPlayer.play()
Timer.scheduledTimer(withTimeInterval: 1.5, repeats: false) {_ in
print("shuffle mode setter")
self.musicPlayer.pause()
//self.musicPlayer.pause()// may stop interface freezing if occuring
self.musicPlayer.shuffleMode = MPMusicShuffleMode.songs // freeze of ui only occurs when no song played before
}
}
I tried out several time intervals it still failed sometimes if it was a second it never failed on 1.5 seconds so I left it there
The problem though was that starting the App as well as restarting it was taking a little bit longer. So I came up with a second solution
private func setShuffleMode2 () { // still in test if shuffle mode gets set fast or even ever set
Timer.scheduledTimer(withTimeInterval: 5, repeats: true) {timer in
if self.musicPlayer.playbackState == .playing && self.musicPlayer.currentPlaybackTime > 3{
self.musicPlayer.shuffleMode = .songs
print("shuffle mode setter")
timer.invalidate()
}
}
}
Here I have a repeating timer which is always checking if an item is playing and if it is playing for a certain time already, if it is it sets the shuffle mode and stops repeating. I have tested the second function and it worked great though there is the trade of that there is always the possibility that it is not getting called for some time. How long that will be depends on the time interval and currentPlaybackTime > someTime value.

How to detect apple watch position using accelerometer and Gravity in swift?

I have creating an application for apple watch. The Logic is, when the user rise their hand and tap a button from app. At that time I will fetch the accelerometer values. And whenever user rise their hand and meet the captured position, I have to send message to iPhone.
For me am getting the values correctly But, It will always give the values based on accelerometer. Which means user doesn't rise the hand but accelerometer values matched. So values will send to mobile.
func startUpadateAccelerometer() {
self.motionManager.accelerometerUpdateInterval = 1.0 / 10.0
self.motionManager.startAccelerometerUpdates(to: OperationQueue()) { (accelerometerData, error) -> Void in
guard accelerometerData != nil else
{
print("There was an error: \(String(describing: error))")
return
}
DispatchQueue.main.async {
if(self.can_reset){
let differenceX : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.x, inititalValue: self.gravityReference.x)
let differenceY : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.y, inititalValue: self.gravityReference.y)
if(differenceX && differenceY && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.x, referenceValue: self.gravityReference.x ) && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.y, referenceValue: self.gravityReference.y)){
WKInterfaceDevice().play(.success)
// self.addLog(_logStr: EventsTypes.Achievements1.rawValue)
self.logString += String(format: "X: %0.3f Y: %0.3f Z: %0.3f \n", accelerometerData!.acceleration.x,accelerometerData!.acceleration.y,accelerometerData!.acceleration.z)
self.m_XYZValueLbl.setText(self.logString)
self.is_RechedZeroPos = true
self.session?.sendMessage(["msg" : "\(self.logString)"], replyHandler: nil) { (error) in
NSLog("%#", "Error sending message: \(error)")
}
} else {
if(self.checkAchievements2_3(deviceMotionData: accelerometerData!.acceleration) == true) {
if self.is_RechedZeroPos == true {
self.addLog(_logStr: EventsTypes.Achievements2.rawValue)
self.is_RechedZeroPos = false
} else {
self.addLog(_logStr: EventsTypes.Achievements3.rawValue)
}
}
}
} else {
self.gravityReference = accelerometerData!.acceleration
//self.logString = String(format: "Reference Acceleration %0.3f %0.3f %0.3f \n", self.gravityReference.x,self.gravityReference.y,self.gravityReference.z)
self.can_reset = true
}
}
}
}
func validateButtom(currentValue : Double , inititalValue : Double) -> Bool {
if( currentValue == 0 && inititalValue == 0) {
return true
} else if( currentValue < 0 && inititalValue < 0) {
return true
} else if( currentValue > 0 && inititalValue > 0) {
return true
} else {
return false
}
}
func gravityOffsetDifference(currentValue : Double , referenceValue: Double) -> Bool {
var difference : Double!
if (fabs(currentValue) <= fabs(referenceValue)) {
difference = fabs(referenceValue) - fabs(currentValue)
} else {
difference = fabs(currentValue) - fabs(referenceValue)
}
if (difference <= gravityOffset ) {
return true
} else {
return false
}
}
Please guide me to get the values only when the user captured the position.
Accelerometers measure changes in velocity along the x, y, and z axes
Fetching accelerometer data
let motion = CMMotionManager()
func startAccelerometers() {
// Make sure the accelerometer hardware is available.
if self.motion.isAccelerometerAvailable {
self.motion.accelerometerUpdateInterval = 1.0 / 60.0 // 60 Hz
self.motion.startAccelerometerUpdates()
// Configure a timer to fetch the data.
self.timer = Timer(fire: Date(), interval: (1.0/60.0),
repeats: true, block: { (timer) in
// Get the accelerometer data.
if let data = self.motion.accelerometerData {
let x = data.acceleration.x
let y = data.acceleration.y
let z = data.acceleration.z
// Use the accelerometer data in your app.
}
})
// Add the timer to the current run loop.
RunLoop.current.add(self.timer!, forMode: .defaultRunLoopMode)
}
}
Important
If your app relies on the presence of accelerometer hardware, configure the UIRequiredDeviceCapabilities key of its Info.plist file with the accelerometer value. For more information about the meaning of this key, see Information Property List Key Reference.

How to programmatically connect 2 players in multiplayer match without presenting the default ViewController

I am working on a real time multiplayer game in IOS using swift language. When I call the function "func findMatchWithMinPlayers(_ minPlayers: Int, maxPlayers: Int)" it shows the default ViewController where we have to invite other player and then the match starts.
Is there any way to do this without presenting the ViewController and to connect 2 players automatically or by ourself?
open class func findMatchWithMinPlayers(_ minPlayers: Int, maxPlayers: Int) {
guard EGC.isPlayerIdentified else {
EGCError.notLogin.errorCall()
return
}
do {
let delegatVC = try EGC.sharedInstance.getDelegate()
EGC.disconnectMatch()
let request = GKMatchRequest()
request.minPlayers = minPlayers
request.maxPlayers = maxPlayers
let controlllerGKMatch = GKMatchmakerViewController(matchRequest: request)
controlllerGKMatch!.matchmakerDelegate = EGC.sharedInstance
var delegeteParent:UIViewController? = delegatVC.parent
if delegeteParent == nil {
delegeteParent = delegatVC
}
delegeteParent!.present(controlllerGKMatch!, animated: true, completion: nil)
} catch EGCError.noDelegate {
EGCError.noDelegate.errorCall()
} catch {
fatalError("Dont work\(error)")
}
}
I have found this solution for programmatically matchmaking but it is in Objective -C
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch *match, NSError *error) {
if (error)
{
// Process the error.
}
else if (match != nil)
{
self.myMatch = match; // Use a retaining property to retain the match.
match.delegate = self;
if (!self.matchStarted && match.expectedPlayerCount == 0)
{
self.matchStarted = YES;
// Insert game-specific code to begin the match.
}
}
}];`
As per your reference above, programmatically finding match and connecting to a specific match are both different processes. As per this official document of Apple, you will have to first find the match. This approach is for programmatically finding a match, and this finding match is not exactly creating a new match, if created for some other user, then would be assigned a slot to you.
let request = GKMatchRequest()
request.minPlayers = 2
request.maxPlayers = 2
GKMatchmaker.shared().findMatch(for: request, withCompletionHandler: {(match, error) -> Void in
if error != nil {
// Process the error.
print("Error finding match")
print(error?.localizedDescription)
self.show_match_error();
}
else if match != nil {
self.myMatch = match!
self.myMatch?.delegate = self
// Use a retaining property to retain the match.
if !self.matchStarted && self.myMatch?.expectedPlayerCount == 1
{
self.matchStarted = true
// Insert game-specific code to begin the match.
print("Match Started")
}
}
else
{
print("Match is nill")
}
})
Now if there's no error neither of network, nor of local player authentication, your matchStarted variable would become true. Now you must have to implement two methods of the delegate GKMatchDelegate. Here are these.
#1
//match state change
func match(_ theMatch: GKMatch, player playerID: String, didChange state: GKPlayerConnectionState) {
/*recall when is desconnect match = nil*/
guard myMatch == theMatch else {
return
}
switch state {
/// Connected /
case .stateConnected where myMatch != nil && theMatch.expectedPlayerCount == 0:
if #available(iOS 8.0, *) {
lookupPlayers();
}
/// Lost deconnection /
case .stateDisconnected:
print("match disconnected")
show_match_error();
matchStarted = false;
default:
break
}
}
#2
// match state change error
func match(_ theMatch: GKMatch, didFailWithError error: Error?) {
guard myMatch == theMatch else {
return
}
guard error == nil else {
print("Match failed with error: \(error?.localizedDescription)")
myMatch?.disconnect()
matchStarted = false;
return
}
}
These methods of the delegate will get you know about the process you did in the first method, in other words you tried to find a real time match for the local player. If without any failure you surpass the stage of creating and connecting to a player in the match, then call the methods lookupPlayers();.
The purpose of lookupPlayers(); is simply escorting connected players with the match you just created, calling this method from the finMatch( .. completion handler, wouldn't work, so the match wouldn't expect to have players in it.
The delegate method did Change State will alarm you about the successful connection of the players. So, you definitely have to call lookupPlayers().
//lookup player
func lookupPlayers() {
guard let match = myMatch else {
print("No Match")
return
}
let playerIDs = match.players.map { $0.playerID }
guard let hasePlayerIDS = playerIDs as? [String] else {
print("No Player")
return
}
//Load an array of players
GKPlayer.loadPlayers(forIdentifiers: hasePlayerIDS) {
(players, error) in
guard error == nil else {
print("Error retrieving player info: \(error!.localizedDescription)")
self.myMatch?.disconnect()
return
}
guard let players = players else {
print("Error retrieving players; returned nil")
return
}
for player in players {
print("Found player: \(String(describing: player.alias))")
}
if let arrayPlayers = players as [GKPlayer]? { self.playersInMatch = Set(arrayPlayers) }
GKMatchmaker.shared().finishMatchmaking(for: match)
//(Static.delegate as? EGCDelegate)?.EGCMatchStarted?()
}
selectHost()
}
The method selectHost() is for selecting host for the match, because at the very first time, this host would send the data or in other words would start the game.
Sending and receiving the data is by these two methods.
// send data
func sendDataToPlayers(data: Data!) {
if(myMatch != nil && matchStarted)
{
let player = myMatch?.players;
do {
try myMatch?.send(data, to: player!, dataMode: GKMatchSendDataMode.reliable)
} catch {
print("Sending failed")
myMatch?.disconnect()
}
}
}
Use the delegate method for receiving the data.
// recieve data
func match(_ match: GKMatch, didReceive data: Data, forRecipient recipient: GKPlayer, fromRemotePlayer player: GKPlayer) {
// CHEERS
}

Array Index Out Of Range - Error when optional unbinding

I have an entity called Settings with an attribute called backgroundColor of type Int, if it is 1 then the view controller will have a background of white if 0 then a background of dark grey.
But I am getting the following error when trying to open the view controller;
fatal error: Array Index out of range
For the following line in my function
if settingsArray.count == 1 {
setting = settingsArray[1]
} else if settingsArray.count <= 0 {
println("No settings in array")
}
View Controller
var settingsArray: [Settings]!
var setting: Settings!
var backgroundSetting: Bool = true
override func viewWillAppear(animated: Bool) {
backgroundSettings()
}
override func viewDidLoad() {
super.viewDidLoad()
backgroundSettings()
}
// Function to fetch settings and change background
func backgroundSettings() {
var error: NSError?
let request = NSFetchRequest(entityName: "Settings")
self.settingsArray = moc?.executeFetchRequest(request, error: &error) as! [Settings]
if settingsArray.count == 1 {
setting = settingsArray[1]
} else if settingsArray.count <= 0 {
println("No settings in array")
}
if setting != nil {
if setting.backgroundColor == 1 {
backgroundSetting = true
} else if setting.backgroundColor == 0{
backgroundSetting = false
}
}
if backgroundSetting == true {
self.view.backgroundColor = UIColor.whiteColor()
} else if backgroundSetting == false {
self.view.backgroundColor = UIColor.darkGrayColor()
}
}
//Button to change the color and settings
#IBAction func backgroundColor(sender: AnyObject) {
if setting != nil {
if setting.backgroundColor == 1 {
setting.backgroundColor = 0
} else {
setting.backgroundColor = 1
}
var error: NSError?
moc?.save(&error)
} else {
println("No settings available")
var settings = NSEntityDescription.insertNewObjectForEntityForName("Settings", inManagedObjectContext: moc!) as! Settings
settings.backgroundColor = 1
var error: NSError?
moc?.save(&error)
}
backgroundSettings()
}
Any ideas where I may be going wrong ?
ETA:
I have been having another think about this. I have left my old answer below so that the previous comments make sense.
In if let thisSetting = settingsArray?[0] … if settingsArray is nil then the right side is potentially effectively nil[0]. Therefore I believe that this may eliminate the crash:
// ...
if !settingsArray.isEmpty {
if let thisSetting = settingsArray?[0] {
setting = thisSetting
}
}
settingsArray[0] being nil would I think then be a separate issue. Which I think would relate to the lifecycle.
Previous answer follows:
I believe that the problem may be being caused by you calling func backgroundSettings() from viewDidLoad() - i.e. too early in the View Controller lifecycle - and before the values have been initialized.
In Swift (as in Objective C and many other languages), the indexes of arrays start at 0. (See this wikipedia article for a list on this.)
This means when you check if settingsArray.count == 1, there will be only (exactly) one item in your list. Since indexes start at 0, this item will be at index 0, hence the Error.
So either you check if settingsArray.count == 2 and leave setting = settingsArray[1], or you change to setting = settingsArray[0].

Resources