Sharing data between swift classes - swift

I have this class called AudioController() and it has a variable called sources which is an array of Strings.
AudioViewController() code:
import Foundation
class AudioController {
static let shared = AudioController()
var sources = [String]()
init() {
print("Sources: \(sources)")
let controller = RemoteCommandController()
player = QueuedAudioPlayer(remoteCommandController: controller)
player.remoteCommands = [
.stop,
.play,
.pause,
.togglePlayPause,
.next,
.previous,
.changePlaybackPosition
]
try? audioSessionController.set(category: .playback)
try? player.add(items: sources, playWhenReady: false) // fatal error here because sources is nil
}
}
But. On my other viewcontroller when trying to pass sources:
AudioController().sources = ["Shakira"]
I get:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
and the print("Sources: \(sources)") returns sources [].

Actually, there are strange things here: why you have a shared instance if you're not using it? And then, you're trying to play an empty array, maybe this is the problem. Try to give values before starting the player
class AudioController {
static let shared = AudioController()
var sources: [String]
init(sources: [String]) {
self.sources = sources
print("Sources: \(sources)")
let controller = RemoteCommandController()
player = QueuedAudioPlayer(remoteCommandController: controller)
player.remoteCommands = [
.stop,
.play,
.pause,
.togglePlayPause,
.next,
.previous,
.changePlaybackPosition
]
try? audioSessionController.set(category: .playback)
try? player.add(items: self.sources, playWhenReady: false) // fatal error here because sources is nil
}
}
// then instantiate the controller with sources
AudioController(sources: ["Shakira"])
However I suggest you to review your design. Maybe it's not a good idea start playing in the init.

If you want to use the shared instance (which I think you do), use something like this:
AudioController.shared.sources = ["Shakira"]
You should also probably add private init() {} to prevent accidentally initializing the AudioController. This will make a private initializer that cannot be used outside of the AudioController.
The print statement will always print [] because when the init function is called, nothing is in the array yet.

Related

Can I Fetch Data from API while using a variable to change the url target? [SwiftUI]

I have a question. I am trying to make a API call to fetch data for a specific item. To do so, I just need to modify 1 parameter of the URL that is making the call.
This is the call on the item page to fetch the data. I feel like I should be able to pass the item # like this. But im having trouble passing it correctly.
struct ProductPage: View {
#ObservedObject var relatedsectionNetwork = RelatedSectionAPI()
...
...
.onAppear() {
relatedsectionNetwork.fetchData(prID: "1824085")
print("relatedSection Loaded")
}
This is the actual class and function that I am using to make the call. You can see what my thought process was here: create a var that could be used to change the prID within the URL.
class RelatedSectionAPI: ObservableObject {
var prID : String = ""
func fetchData() {
print("fetchData3 -start 􀋂")
if let url = URL(string:"https://www.*****.com/****/******?***=***=&***=&***=***&pr_id=\(prID)&***") {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error == nil {
let decoder = JSONDecoder()
I cut this part short to not confuse anyone.
If any one can please help me out, or point me in the right direction, it would be greatly appreciated!
Thank you
Add the parameter to the function call func fetchData(prID: String = ""). BTW you should use #StateObject vs #ObservedObject when you initialize an ObservableObject in a View

Trouble using swift 4's KVO "observe" instead of addObserver

I'm having some trouble getting the new KVO syntax right.
According to the Apple documentation:
Create an observer for the key path and call the
observe(_:options:changeHandler) method. For more information on key
paths, see Keys and Key Paths.
class MyObserver: NSObject {
#objc var objectToObserve: MyObjectToObserve
var observation: NSKeyValueObservation?
init(object: MyObjectToObserve) {
objectToObserve = object
super.init()
observation = observe(\.objectToObserve.myDate) { object, change in
print("Observed a change to \(object.objectToObserve).myDate, updated to: \(object.objectToObserve.myDate)")
}
}
}
let observed = MyObjectToObserve()
let observer = MyObserver(object: observed)
observed.updateDate()
I'm initializing my observation like so:
self.observation = self.webView!.observe(\.webView.isLoading, changeHandler: { (webView, observedChange) in
//code
})
but am getting this error:
Turns out the syntax needs to be like this, using the object Type rather than the object instance name:
self.observation = self.webView!.observe(\WKWebView.isLoading, changeHandler: { (webView, observedChange) in
//code
})
Misread the documentation ¯\_(ツ)_/¯
If you use the \. syntax the root element is the observed object so it's simply
self.observation = self.webView!.observe(\.isLoading, ...
The compiler treats your syntax as webView.webView.isLoading which is obviously not intended.

Modifying struct instance variables within a Dispatch closure in Swift

I'm using the DEVELOPMENT-SNAPSHOT-2016-06-06-a version of Swift. I cannot seem to get around this issue, I've tried using #noescape in various places, but I still have the following error:
Closure cannot implicitly capture a mutating self parameter
To better explain, here is a simple example:
public struct ExampleStruct {
let connectQueue = dispatch_queue_create("connectQueue", nil)
var test = 10
mutating func example() {
if let connectQueue = self.connectQueue {
dispatch_sync(connectQueue) {
self.test = 20 // error happens here
}
}
}
}
Something must have changed in these Swift binaries that is now causing my previously working code to break. A workaround I want to avoid is making my struct a class, which does help in fixing the issue. Let me know if there is another way.
I cannot test it, because I'm not using a build with that error, but I'm pretty sure by capturing self explicitly you can fix it:
dispatch_sync(connectQueue) { [self] in
self.test = 20
}
EDIT: Apparently it doesn't work, maybe you can try this (not very nice tbh):
var copy = self
dispatch_sync(connectQueue) {
copy.test = 20
}
self = copy
If you want to read more on why, here is the responsible Swift proposal.
The new dispatch API makes the sync method #noreturn so you wouldn't need the explicit capture:
connectQueue.sync {
test = 20
}
You are using Swift3 since you mentioned a recent dev snapshot of Swift. Try below and let me know if it works:
public struct ExampleStruct {
let connectQueue = DispatchQueue(label: "connectQueue", attributes: .concurrent)//This creates a concurrent Queue
var test = 10
mutating func example() {
connectQueue.sync {
self.test = 20
}
}
}
If you are interested in other types of queues, check these:
let serialQueue = DispatchQueue(label: "YOUR_QUEUE", attributes: .serial)
serialQueue.sync {
//
}
Get the mainQueue asynchronously and synchronously:
DispatchQueue.main.async {
//async operations
}
DispatchQueue.main.sync {
//sync operations
}
And if you are interested in Background:
DispatchQueue.global(attributes: .qosDefault).async {
//async operations
}
You could refer this for new features in Swift3 and for changes to existing version: Migrating to Swift 2.3 or Swift 3 from Swift 2.2

PLIST read/write with generic dictionary in Swift

I'm trying to add load/save functions to a generic caching class, like this:
class Cache<T:Hashable, U:AnyObject> {
private var cache: [T:U] = [:]
private var url: NSURL!
func load() {
if let path = self.url where NSFileManager().isReadableFileAtPath(path.path!) {
if let dict = NSDictionary(contentsOfURL: path) {
// The next line is rejected by the compiler as always failing the cast
cache = (dict as? DictType)!
}
}
}
func save() {
if let path = self.url where NSFileManager().isWritableFileAtPath(path.path!) {
// The next line is rejected by the compiler as the method not existing
let result = cache.writeToURL(path, atomically: false)
}
}
.
.
.
}
If I use concrete types this works, but translating to generics apparently takes away enough information that the compiler is no longer willing to accept it. Is there a way to do this?
I also tried looping through the NSDictionary, but for example the loop in the load routine then complains it doesn't have a type for the key (and won't accept what's returned from allKeys); nor can I cast / coerce to the type T.

Swift gives "self used before all stored procedures are initialized" error when building child nodes

In XCode 6.2, I have a Swift project where a main-object ("Backbone") creates sub-objects with pointers back to Backbone:
class Backbone
{
let logManager: QCLogManager!
let cloudJobManager: CloudJobManager!
...
init() {
logManager = QCLogManager(backbone: self)
cloudJobManager = CloudJobManager(backbone: self)
...
}
It works very nicely. However, in XCode 6.3 each line in init() now gives the error:
'self' used before all stored properties are initialized.
That seems a rather unhelpful compiler restriction. Why did it change? How should I now be doing it?
let logManager: QCLogManager!
let cloudJobManager: CloudJobManager!
if let is not necessary, change to var
var logManager: QCLogManager!
var cloudJobManager: CloudJobManager!
You might need to create a property of type Backbone in both QCLogManager and CloudJobManager and set that property after initialization of all store properties of BackBone class.
class Backbone
{
let logManager: QCLogManager!
let cloudJobManager: CloudJobManager!
...
init() {
logManager = QCLogManager()
cloudJobManager = CloudJobManager()
...
logManager.backBone = self
cloudJobManager.backBone = self
}