How to transfer data using MultipeerConnectivity? - ios

I'm trying to transfer an array of MPMediaItem(s) to another device using MultipeerConnectivity, so I can show a list (TableView) of songs and therefore remote control the peer.
This piece of code encodes my music library items so I can send them to another peer.
func encodeLibrary(lib: [MPMediaItem]) -> NSData {
print("before encoding: \(lib.count)")
// --> prints "before encoding: 511"
let data = NSMutableData()
let archiver = NSKeyedArchiver.init(forWritingWithMutableData: data)
archiver.encodeObject(lib, forKey: "data")
archiver.finishEncoding()
let unarchiver = NSKeyedUnarchiver.init(forReadingWithData: data)
let newLib = unarchiver.decodeObjectForKey("data") as! [MPMediaItem]
print("decoded: \(newLib.count)")
// --> prints "decoded: 511"
return data
}
The following code is then executed on another peer:
func decodeLibrary(data: NSData) -> [MPMediaItem] {
let unarchiver = NSKeyedUnarchiver.init(forReadingWithData: data)
let lib = unarchiver.decodeObjectForKey("data") as! [MPMediaItem]
print("items: \(lib.count)")
//prints "items: 0"
return lib
}
To send the data I use the following call:
try! session.sendData(data, toPeers: [peerID], withMode: .Reliable)
It's not a problem with the en-/decoding because it works when i run the decoding on the same device right after the encoding as you can see. It prints 511 songs before and after.
There has to be a problem while being transmitted or anything I can't think of why.
When data arrives on another device, everything except these MPMediaItems is available as well.
I do not receive any errors and other parts of the communication are working fine. Just this one array does not seem to be available on other devices. Any idea how to fix this?
Thanks in advance,
KlixxOne
EDIT: Actually the array is there, but it has no content (which it had on the other device (511 entries)).

Related

Firestore in Swift - Listener producing needless reads

I have an app that uses a snapshot listener to listen to data in a particular document. However, when a field in the document is updated, the data is read 7-10x over. Never read once, and never read the number of fields that are in my document, it always seems to be an arbitrary number. Also, when the read data prints out, it seems like every printout is the same except for a couple of fields that I'm not setting (like an array prints out "<__NSArrayM 0x282d9f240>" but the number changes on each print). As a result, minimal usage of my app is causing 5-10k reads. I'm trying to reduce the number of reads and I don't know exactly how, but the app has to read as data is updated, but my two questions are:
when I print the data from the listener, does each data print out signify a separate read operation? and
is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated, then perform one read instead of multiple reads every time any field is updated? Or another strategy to reduce reads when multiple writes occur?
Not sure if this is helpful, but here is the code I'm using to perform the read...its pretty much the standard code from the firestore sdk:
env.db.collection(env.currentSessionCode!).document(K.FStore.docName).addSnapshotListener { [self] documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching snapshot: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
self.env.data1 = data[K.FStore.data1] as? String ?? "????"
self.env.data2 = data[K.FStore.data2] as? String ?? "????"
self.env.data3 = data[K.FStore.data3] as? [String] ?? ["????"]
self.env.data4 = data[K.FStore.data4] as? [String] ?? ["????"]
self.env.data5 = data[K.FStore.data5] as? Double ?? 0
self.env.data6 = data[K.FStore.data6] as? Double ?? 0
self.env.data7 = data[K.FStore.data7] as! Bool
self.env.data8 = data[K.FStore.data8] as! Bool
print("Current data: \(data)")
Update - For clarification, the way I have been updating my data to firebase is with a environment object, and using "didSet" when the new data is changed/updated in the environment to update it on firebase...I think this might be the root of the problem, as the function called on didSet runs 4-5 times each time it is called...
relevant code:
#Published var data1: String {
didSet {
postValuesToFB(fb: K.FStore.data1, string: data1)
}
}
func postValuesToFB(fb: String, string: String) {
guard let code = currentSessionCode else {
fatalError("Error - Connection Check - no value for current session code in Global Env")
}
let docRef = db.collection(code).document(K.FStore.docName)
docRef.getDocument { document, _ in
guard let document = document else {
return
}
if document.exists {
let session = self.db.collection(code).document(K.FStore.docName)
session.updateData([
fb: string,
K.FStore.dateLastAccessed: FieldValue.serverTimestamp(),
])
return
}
}
}
Based on your comments, it sounds as if you've written no code to remove a listener after it's been added. Based on this, it's relatively safe to assume that your code could be adding many listeners over time, and each one is getting called for each change.
You should take a moment to think about the architecture of your app and figure out when is the appropriate time to remove listeners when they're no longer needed. Usually this corresponds with the lifecycle of whatever component is responsible for display of the data from the query. Review the documentation for getting realtime updates, especially the section on detaching a listener. It's up to you to determine the right time to remove your listener, but you definitely don't want to "leak" a listener as you are now.
A common source of unexpected read charges for developers who are new to Firestore is the Firebase console itself. When that console displays Firestore content, you are charged for those read too. To ensure you measure the impact of your code correctly, test it with the Firebase console closed.
when I print the data from the listener, does each data print out signify a separate read operation?
Not really. You get charged for a document read, when the document is read on your behalf on the server. You are not charted for printing the same DocumentSnapshot multiple times.
is there any way for the listener to be alerted of the update but wait to actually perform the read until the data is updated
Nope. To know the document has changed, the server needs to read it. So that requires a charged read operation.

Using CoreBlueTooth L2CAP channel to transfer an audio file

I need some sample code to see how to transfer an audio file (or any other binary data) using CoreBlueTooth L2CAP channel. Assuming the file is not so small, let us say it is a few hundred kilobytes.
I am working on a small iOS app doing that, but it is only working half way.
At this point I can transfer a few thousand bytes, but it does not go further.
Just in case, this is the related code I have on the sending side:
let path = Bundle.main.path(forResource: "\(name)", ofType: nil)!,
url = URL(fileURLWithPath: path)
do {let audioData = try Data(contentsOf: url)
// do something useful with audioData to send it
// to the other device.
..........
let bytesWritten = data.withUnsafeBytes {outStream!.write($0, maxLength: audioData.count)}
if bytesWritten > 0 {
..........
}
} catch {
print("Error: \(error.localizedDescription)")
}
On the receiving side:
let inData = Data(reading: inStream)
if inData.count != 0 {
// Data has been received.
.......
}
I am obviously not showing much detail, but my code is missing some critical parts anyway, this is why I would be glad to find some little working sample in order to see how it works.

Data in my iOS app vanishes after a couple of days

I have an iPhone app that stores data locally in an array. I was testing the app on my actual iPhone and it works fine except that the data vanishes after a couple of days. I am curious to see if this has happened to anyone else and if a solution was found. Here is a sample of the code that stores the data:
var doctorsArray = [Doctors]()
func storeDoctorsArray() {
doctorsArray.sort(by: {(a: Doctors, b: Doctors) -> Bool in
return a.firstName < b.firstName
})
let defaults = UserDefaults.standard
let data = NSKeyedArchiver.archivedData(withRootObject: doctorsArray)
defaults.set(data,forKey: "stored_doctors_data")
defaults.synchronize()
}
And to get data:
func loadDoctorsArray() {
if let storedArray = UserDefaults.standard.object(forKey: "stored_doctors_data") as? Data {
doctorsArray = NSKeyedUnarchiver.unarchiveObject(with: storedArray) as! [Doctors]
Again as mentioned, the app works fine and the data stores and displays as expected but vanishes after a couple of days.
If you delete the app it clears the contents of UserDefaults. Other than that, or resetting the simulator, UserDefaults should persist between runs.
How/when are you calling your loadDoctorsArray() function

Build time is too long in Xcode 8.3 with swift 3 in particular file

Im using Xcode 8.3 with Swift 3. I have written one method named pdfFromData(data:) to form the pdf document from the Data, whenever I build my project its not getting build due to this method, means the compiler is got stopped/hanged when it start compile particular file where I coded pdfFromData(data:) method(In Xcode 8.2 with Swift 3 it worked fine). Whenever i comment this method and build means everything working fine.
func pdfFromData(data: Data) -> CGPDFDocument? { // Form pdf document from the data.
if let pdfData = data as? CFData {
if let provider = CGDataProvider(data: pdfData) {
let pdfDocument = CGPDFDocument(provider)
return pdfDocument
}
}
return nil
}
What's wrong with this method?. I want to build my project with this method as well. Thanks in advance.
I tried debugging your issue. This is what I found out:
if let pdfData = data as? CFData {
}
The above line for casting object of type Data to CFData is where it's taking too much time to build.
Replacing that with the following piece of code significantly reduces your build time.
let pdfNsData: NSData = NSData(data: data) // convert `Data` to `NSData`
if let cfPdfData: CFData = pdfNsData as? CFData {
// cast `NSData` to `CFData`
}
NSData and CFData are toll-free bridged.
Please let me know if there's any doubt

Fetching from SQL file using Core Data works only on a single simulator

This is what I did:
Filled an SQL database using Core data and a parser from a CSV file.
This was only a one time fill. It will never be needed to repeat
this.
Successfully fetched data from this SQL database using iPhone SE simulator.
Changed simulator to anything else than iPhone SE.
I am not able to get any data from the database. I always get zero results (empty arrays), as if there were no matching data in the database.
If I change the simulator back to iPhone SE, it works again.
I think it must be something with being able to connect to the SQL database. Here is the simplified fetchData func I am using:
func fetchData() -> [[Any]]{
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "FullEntity")
let coreDataStack = CoreDataStack()
//CoreDataStack class from a different file. It defines persistent container.
request.returnsObjectsAsFaults = false
let context = coreDataStack.persistentContainer.viewContext
do{
let results = try context.fetch(request)
//results are transformed into array here
}catch{
print("Some error")
}
return results
}

Resources