Post

Replies

Boosts

Views

Activity

Reply to Using an excel document as a database/dictionary in Swift
In SwiftUI, ContentView is the main user interface, equivalent to Main ViewController in UIKit. The List command (actually a struct in SwiftUI) produces a Table on the UI. List(model.dataTable.rows,id:\.index) { row in // this creates a Table of entries from dataTable (i.e. Terms and phrases             HStack{ // this contains formatting for the rows of the table                 Text(row["Term"] as! String) // this is the Term "cell", although it's not a cell like in UIKit                 Text(String(row["Phrase"] as! String)) // this is the Phrase cell, although it's not a cell like in UIKit             }         } That's all that's needed in SwiftUI to create a working UI for your Term/Phrase data. However, in Xcode (when creating a project) you have to make sure that the Interface option for a new project specifies SwiftUI not Storyboard. With UIKit, processing of data for a View is normally done within the ViewController of the View. With SwiftUI, the data processing is preferably done in a Data Model with results made available to SwiftUI views via (for example) the var model = DataModel.shared statement. So, for the Search Bar, the query text would be provided to the Data Model, which runs the query, then provides the result(s) back to the SwiftUI View. If I get time later today (it's 8:30am now here in Oz) I'll extend my sample code to include searching. Best wishes and regards, Michaela
Topic: Programming Languages SubTopic: Swift Tags:
Mar ’22
Reply to Using an excel document as a database/dictionary in Swift
The answers to your questions depend what you want to do with the "Dictionary", how volatile it is (i.e. updates, additions, deletions) and how large it is. A few hundred Terms that never change could be handled without a database (i.e. without an indexed, structured permanent store such as SQLite or CoreData). From my understanding of what you're looking for, I'd suggest creating your terms/phrases in a spreadsheet (e.g. Excel) and then exporting the table as a CSV file for use with code like in the SwiftUI sample below: ContentView import SwiftUI import TabularData struct ContentView: View {     var model = DataModel.shared     var body: some View {         List(model.dataTable.rows,id:\.index) { row in             HStack{                 Text(row["Term"] as! String)                 Text(String(row["Phrase"] as! String))             }         }     } } Data Model - this is not a database: it's where the importing and processing of the terms takes place within the app, and the data have to be loaded into the app again when next run. import Foundation import TabularData class DataModel {     static let shared = DataModel()      @Published var dataTable = DataFrame()     init() {         getData()         let searchTerm = "Nine"         print("there are \(findTerm(searchTerm).rows.count) terms for \(searchTerm)")     }     func getData() {         var url: URL?           do {              url = try FileManager.default.url(for: FileManager.SearchPathDirectory.downloadsDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: true)         } catch {               print("Failed to get Downsloads URL \(error)")             return         }                      let csvOptions = CSVReadingOptions(hasHeaderRow: true, ignoresEmptyLines: true, delimiter: ",")         let fileURL = url?.appendingPathComponent("TermPhrases.csv")                do {             dataTable = try DataFrame(contentsOfCSVFile: fileURL!,columns: nil, rows: nil, types: ["Term":CSVType.string,"Phrase":CSVType.string], options: csvOptions)             } catch {                 print("Failed to load datatable from CSV \(error)")                 return         }     }     func findTerm(_ searchTerm: String) -> DataFrame.Slice {         let results = dataTable.filter ({ row in             guard let term = row["Term"] as? String else {                 return false             }             if term == searchTerm {                 return true             }             return false         })         return results     } } extension DataFrame.Rows : RandomAccessCollection { } Thiis sample uses the TabularData Framework which has many features for importing and manipulating data from CSV files (spreadsheet output), but also for Machine Learning. This sample app was written for MacOS using csv input from the Downloads folder: Sample Data in a file called TermPhrases.csv Term Phrase Nine A stitch in time saves these Two A bird in the hand is worth these in the bush Seven Pillars of wisdom The findTerm function in the DataModel shows how to filter (Search) the Data Table for a term. DataFrame filtering is not like normal Swift array filtering. Hopefully this gets you started, then you can decide what other processing you'd like to do on the Data Table (i.e. your dictionary) Best wishes and regards, Michaela
Topic: Programming Languages SubTopic: Swift Tags:
Mar ’22
Reply to Fill CMAmbientPressureData
My understanding is that CMAmbientPressureData is an internal class, which is then used by CMAltimeter to calculate relative altitude based on pressure changes. The data from CMAltimeter.startRelativeAltitudeUpdates has a pressure property that provides the atmospheric pressure, as an NSNumber of kilopascals, of the reported relative altitude. As of iOS 15 there is a CMAbsoluteAltitudeData class that provides the absolute altitude (and changes thereof), but I can't see a pressure property available via the updates handler. I've been using CMAltimeter data on a regular basis for a couple of years in my running app and the update handler consistently provides an update every second, irrespective of an altitude (or pressure) change or not. Most of my runs start and end at the same location, so I often see a discrepancy of 2 to 5 metres between the start and end relative altitudes - which coincide with the atmospheric pressure change at that location as recorded by the Bureau of Meteorology. I haven't accessed the pressure property of CMAltitudeData but, from the foregoing, I conclude that it would be a fairly accurate recording of the current ambient pressure of a location. For my purposes, the CMAltimeter data are far more accurate and consistent than the altitude data from CoreLocation. I hope this helps. Best wishes and regards, Michaela
Topic: App & System Services SubTopic: Core OS Tags:
Mar ’22
Reply to How do I access stored properties from another UIViewController programmatically?
I haven't used UIKit since SwiftUI arrived, but the below code should work. Create a DataModel class for use as a singleton and put your items array into it: import Foundation class DataModel {     static let shared = DataModel()     var items: [String] = [] } In your FirstViewController add a reference to the DataModel singleton, then use the items array as needed, e.g. in your UITableView let dataModel = DataModel.shared Use dataModel.items in the UITableView In the SecondViewController also add a reference to the DataModel singleton, then use this to append to items: class SecondViewController: UIViewController { let dataModel = DataModel.shared let textField: UITextField = UITextField() let saveButton: UIButton = UIButton() @objc func saveButtonTapped() { dataModel.items.append(textField.text) } } However, using this method you then, presumably, need to force a reload of the FirstViewController's tableview. This can be done via delegation (which could also be used to send the new item for appending in the FirstViewController) or via Combine. If using Combine, the DataModel becomes: import Foundation import Combine class DataModel {     static let shared = DataModel()     let itemAdded = PassthroughSubject<(String), Never>()         var items: [String] = [] {         didSet {             itemAdded.send("ItemAdded")         }     } } and the FirstViewController needs to include Combine and to have var itemSink : AnyCancellable? = nil itemSink = dataModel.itemAdded.sink(receiveValue: { message in // you don't need to use message, but can be useful if you want to know about additions, deletions etc by sending different messages             tableView.reloadData()         }) at an appropriate place in your code, i.e. to force the Table Reload. I haven't tested this with a real UIKit app and tableview, but it should work. Regards, Michaela
Topic: UI Frameworks SubTopic: UIKit Tags:
Mar ’22
Reply to Silently Update Published Data
I'm not sure that I fully understand what you're doing and how you're doing it. However, I've a couple of apps that have numerous variables in my Data Store (backend?) updated by async methods (e.g. BLE devices) and prefer not to overload my SwiftUI Views with extraneous updates. I therefore use the Combine Framework to publish an object when it's ready for display e.g. in my Data Store  public let transactionAdded = PassthroughSubject<(Transaction), Never>(). Then when appropriate, in my Data Store code, I publish the object e.g. transactionAdded.send(newTransaction) In my SwiftUI View I use .onReceive to listen for the published object and do whatever is necessary e.g. .onReceive((dataStore.transactionAdded), perform: { transaction in .......}. The perform usually needs to trigger a View refresh, e.g. by setting/updating a @State variable. That said, Combine can update values in the store without triggering view updates, and/or chain async results before then informing views. Dunno if this helps. Cheers, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to How to get current user location title to display
I assume that you want to show the location's coordinates as a place name: if so, you need to use CoreLocation's CLGeocoder class, which returns an array of Placemarks. i.e. name and address. Usually there will be only one place mark for a CLLocation (i.e. coordinates), but sometimes there can be several, e.g. when a location has a number of businesses. There are plenty of examples on the Web of how to use CLGeocoder, but if you get stuck I can provide some sample code. Best wishes and regards, Michaela
Topic: Programming Languages SubTopic: Swift Tags:
Mar ’22
Reply to Orientations please
Could you explain a bit more about what you're trying to achieve and what you mean by "inclusive help"? Also, are you able to say more about @MasEmpathyCommunity (assuming that it is a community-based programme)? I'm a retired IT Professional (50+ years experience) and still developing apps, using latest Apple technology, but mainly for my own / family / friends use, or for a worthwhile public app, e.g. symptom tracker for a rare cancer. I don't claim to be an Apple expert, but can usually figure things out as needs be from reading this forum and Stack Overflow. I'd be happy to assist you, if I'm able, once I've better understood your needs. Best wishes, Michaela (in Australia)
Mar ’22
Reply to Accessing array values in published variable fails beyond a single value
The problem is in the initialisation of your @Published var pp: [[CGFloat]] = [[0]], because this is initialising pp with a single 0 value - to which you append tuples in updateP. Changing it to @Published var pp = [[CGFloat]]() fixes the problem. If you want to start with [0,0], then change the initialisation of pp to be @Published var pp: [[CGFloat]] = [[0,0]] Best wishes and regards, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to I have had this same error for a few days and I can't fix it
Here's an example of ForEach with an enum: The enum enum WordLength : Int, CaseIterable {     case random = 0     case five = 5     case six = 6     case seven = 7 } The ForEach ForEach(WordLength.allCases,id:\.self) { wordLength in         Text(String(describing:wordLength).capitalized)  } The enum doesn't have to be Int: I've taken this example from an app that uses the numeric value (Int) in processing, whereas in the ForEach it's for showing Picker options as text (string). Regards, Michaela
Topic: Programming Languages SubTopic: Swift Tags:
Mar ’22
Reply to TabularData Framework: DataFrame as a List in SwiftUI
Here's a working example for Mac OS (but should be the same for iOS except for the URL setup for the incoming csv file). The csv test data are in 3 columns, with headers of "Name", "Position" and "Score" - so as to test data types of String, Integer and Double. The Data Model (ViewModel) import Foundation import TabularData class DataModel {     static let shared = DataModel()     @Published var dataTable: DataFrame?     init() {         getData()     }      func getData() {         var url: URL?         do {              url = try FileManager.default.url(for: FileManager.SearchPathDirectory.downloadsDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: true)         } catch{               print("Failed to get Downsloads URL \(error)")             return         }         let csvOptions = CSVReadingOptions(hasHeaderRow: true, ignoresEmptyLines: true, delimiter: ",")         let fileURL = url?.appendingPathComponent("TestCSV.csv")                  do {             dataTable = try DataFrame(contentsOfCSVFile: fileURL!,columns: nil, rows: nil, types: ["Name":CSVType.string,"Position":CSVType.integer,"Score":CSVType.double], options: csvOptions)             } catch {                 print("Failed to get load datatable from CSV \(error)")                 return         }     } } extension DataFrame.Rows : RandomAccessCollection { } SwiftUI ContentView import SwiftUI import TabularData struct ContentView: View {     var model = DataModel.shared     var body: some View {         List(model.dataTable!.rows,id:\.index) { row in             HStack{                 Text(row["Name"] as! String)                 Text(String(row["Position"] as! Int))                 Text(String(row["Score"] as! Double))             }         }     } } With forced unwrap (as!) the app will crash if the processed column does not contain the correct type, so I tend to use a function that checks and returns a string - which then gets used in the View: // in Data Model func columnToString(_ column: Any) -> String {         if let str = column as? String {             return str         }         if let int = column as? Int {             return String(int)         }         if let double = column as? Double {             return String(double)         }         return ""     } // a row in ContentView Text(model.columnToString(row["Position"] as Any)) I hope this helps. Regards, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to Help with first watch app
There are 4 methods for phone/watch communication under Watch Connectivity, each related to the amount/type of data and the required immediacy of transfer. Now that I better understand what you're doing, my perception is that your use-case needs the sendMessage method, which you seem to have done, rather than transferUserInfo (which is simpler to implement and more reliable, but less immediate). However there are some potential issues with sendMessage from the watch to the phone if the phone is not reachable or not active, as reported here (as recently as 3 months ago). Advances in iOS over the last year or two have improved the ability for background processing (in essence, when the app is not active on screen) - but I haven't had cause to delve into it and so can't offer advice. I suggest, if you haven't already done so, that you test your watch/phone/autopilot communication with your phone (phone app) in various states - to ensure that the watch's commands to the autopilot are reliably implemented. Best wishes and regards, Michaela
Topic: App & System Services SubTopic: General Tags:
Mar ’22
Reply to MacOS MapKit - CVDisplayLinkSetPaused alternating true/false several times per second
It's still happening with Xcode Version 13.3 beta 2 (13E5095k) and Monterey 12.3 Beta (21E5227a). I tried running a working iPad app under Mac Catalyst with MKMapView as UIViewRepresentable. It also shows the messages AND then crashes with __validateTextureView:557: failed assertion `Texture View Validationtexture usage (0x05) doesn't specify MTLTextureUsagePixelFormatView (0x10)'_. My understanding is that MapView uses Metal for its rendering, hence the messages re syncing screen refresh, and requires the MLTextureUsagePixelFormatView to be set. However, since everything works fine on iOS (iPad), it appears that there's a problem with the Mac OS and Catalyst implementation of MapView under SwiftUI. I tried adding (blindly) MetalLayer and Metal Device instances and required settings to my code, but to no avail. When I get some spare time I'll collect all the evidence and submit a bug report. Cheers, Michaela
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Mar ’22
Reply to Help with first watch app
I helped with a similar question a few months ago in this post https://developer.apple.com/forums/thread/689484?answerId=686982022#686982022 If your iPhone app is in SwiftUI, the sample code I provided should form a workable basis for your use case. Note that the phone app and watch app both use a Data Model (i.e. standalone class, not a SwiftUI View) for communicating with each other and processing the data. The models also provide and collect data from their respective SwiftUI Views, i.e the user presses a button or enters data on the device in a SwiftUI view, which then gets collected by the Data Model and processed and/or transmitted to the other device. In my sample code (above) the user wanted to transmit count data from the watch: in your case you (presumably) want to transmit a command to the phone. You can do this by having a common struct e.g named Command, in each Data Model, that describes the command that you're passing. Then in the transferUserInfo (i.e. send) and didReceiveUserInfo (receive) functions you use an instance of Command to send/receive your navigation instructions. Having received the Command from the watch, your iPhone Data Model would then send the appropriate instructions to the BLE autopilot. My original post is a very simplified version of watch connectivity and there are circumstances where things can get more complicated. I therefore advise that, at some point, you read the documentation link I provided earlier. I hope that this helps. Regards, Michaela PS - it's also possible for the watch to communicate directly with your BLE navigation device, using the same code as on the iPhone (except for the UI, given the watch's small screen): but maybe that's another question/answer for another day. Second PS - depending on how the iPhone app and device are configured, the phone app might not on-send the command to the autopilot immediately - which could be problematic!!! My suggestion therefore is that your watch app communicates directly with the autopilot, as per the phone app.
Topic: App & System Services SubTopic: General Tags:
Mar ’22