I've been trying to save a selected color with UserDefaults from UIColorPickerViewController. But I run into a color space fiasco. Anyway, here come my lines of code.
class ViewController: UIViewController, UIColorPickerViewControllerDelegate {
@IBOutlet weak var imageView: UIImageView!
@IBAction func selectTapped(_ sender: UIButton) {
let picker = UIColorPickerViewController()
picker.delegate = self
picker.selectedColor = .yellow
picker.supportsAlpha = false
present(picker, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let color = UserDefaultsUIColor.shared.readColor(key: "MyColor") {
print("Color being read: \(color)")
}
}
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
let color = viewController.selectedColor
print("Selected color: \(color)")
UserDefaultsUIColor.shared.saveColor(color: viewController.selectedColor, key: "MyColor")
}
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
imageView.backgroundColor = viewController.selectedColor
}
}
class UserDefaultsUIColor {
static let shared = UserDefaultsUIColor()
func saveColor(color: UIColor, key: String) {
let userDefaults = UserDefaults.standard
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData?
userDefaults.set(data, forKey: key)
} catch {
print("Error UserDefaults: \(error.localizedDescription)")
}
}
func readColor(key: String) -> UIColor? {
let userDefaults = UserDefaults.standard
if let data = userDefaults.data(forKey: key) {
do {
if let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
return color
}
} catch {
print("Error UserDefaults")
}
}
return nil
}
}
I first start out with a yellow color (UIColor.yellow). And I select a color whose RGB values are 76, 212, 158, respectively. And the color picker guy returns the following.
kCGColorSpaceModelRGB 0.298039 0.831373 0.619608 1
And I get the following in reading the saved color data object.
UIExtendedSRGBColorSpace -0.270778 0.84506 0.603229 1
How can I save and read color data objects consistently? I could specify a color space when I save a color. But it doesn't go well.
Muchos thankos
Señor Tomato de Source
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I have some lines of code below where I can make multiple selections.
import SwiftUI
struct ContentView: View {
@State private var selectedUsers: Set<String> = []
@State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"]
var body: some View {
VStack {
List(selection: $selectedUsers) {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.environment(\.editMode, .constant(.active))
}
}
}
So the blue selection symbol appears as shown in the screenshot above. That's good. But that's not what I'm after. I just want to select one row at a time.
import SwiftUI
struct ContentView: View {
@State var selectedUser: String?
@State var users = ["Susan", "Kate", "Natalie", "Kimberly", "Taylor", "Sarah", "Nancy", "Katherine", "Nicole", "Linda", "Jane", "Mary", "Olivia", "Barbara"]
var body: some View {
VStack {
List(selection: $selectedUser) {
ForEach(users, id: \.self) { user in
Text(user)
}
}
.environment(\.editMode, .constant(.active))
}
}
}
In the lines of code above, I only let myself select one row at a time. And I don't get the blue selection symbol. I wonder why? I find two or three websites where they have similar lines of code and where they select one row at a time. And they have the blue selection symbol. Why don't I get it?
Mucho thankos for reading.
Oh, boy... Xcode has become more and more difficult to deal with. Today, I've dowloaded Version 15.0 beta 4. It took my 2019 iMac with 64 GB of RAM some 20 minutes just to launch an iPhone 14 Simulator and to let me see the home screen. Xcode takes 3 or 4 minutes to run code after I change just one line. I only have some 30 lines of code in total. It's a truly disappointing update. I wish they stop adding unnecessary features like code-folding animation to slow things down.
import UIKit
class ViewController: UIViewController {
private let photoView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(systemName: "airplane")
//imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
view.addSubview(photoView)
NSLayoutConstraint.activate([
photoView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
photoView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
photoView.widthAnchor.constraint(equalToConstant: 200),
photoView.heightAnchor.constraint(equalToConstant: 200)
])
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.runAirplaneAnimation()
}
}
func runAirplaneAnimation() {
photoView.addSymbolEffect(.pulse, animated: true)
}
}
I have the following lines of code to list some music titles from iTunes music. The code is 100% reproducible.
import SwiftUI
struct MusicView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
MusicListView(viewModel: viewModel)
}
}
struct MusicListView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
NavigationView {
List(viewModel.results, id: \.self) { result in
VStack(alignment: .leading) {
Text("Track ID: \(result.trackId)")
Text("Track name: \(result.trackName)")
}
}
.task {
do {
try await viewModel.fetchMusic()
} catch SessionError.badURL {
print("Bad URL")
} catch SessionError.invalidHTTPResponse {
print("Invalid HTTP response")
} catch SessionError.error(let err) {
print("Error: \(err)")
} catch {
print("\(error.localizedDescription)")
}
}
.navigationTitle("Music")
}
}
}
class ViewModel: ObservableObject {
@Published var results: [Result] = []
func fetchMusic() async throws {
guard let url = URL(string: "https://itunes.apple.com/search?term=classical+music&entity=song") else {
throw SessionError.badURL
}
let urlRequest = URLRequest(url: url, timeoutInterval: 0.00) // <<<<<<<<<<<<<
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
do {
guard let data = data, error == nil else {
throw SessionError.noData
}
guard let httpResponse = response as? HTTPURLResponse else {
throw SessionError.invalidHTTPResponse
}
switch httpResponse.statusCode {
case 200:
let res = try JSONDecoder().decode(Response.self, from: data)
DispatchQueue.main.async {
self.results = res.results
}
case 400...499:
throw SessionError.badURL
default:
fatalError()
break
}
} catch {
print(error.localizedDescription)
}
}
.resume()
}
}
struct Response: Codable {
let resultCount: Int
let results: [Result]
}
struct Result: Codable, Hashable {
var trackId: Int
var trackName: String
var collectionName: String
}
enum SessionError: Error {
case badURL
case noData
case decoding
case invalidHTTPResponse
case badRequest(statusCode: Int)
case redirection(statusCode: Int)
case server(statusCode: Int)
case error(String)
}
As you see in the screenshot, I get some music titles listed.
My question is why I get a list when in fact I have the URLRequest's timeout value set to 0.00? I haven't run it with an actual device. As far as I use an iPhone simulator, regardless of the timeout value that I set, I get data downloaded. I wonder why?
Muchos thankos for reading
I have followed a tutorial written by Hacking with Swift ( https://www.hackingwithswift.com/books/ios-swiftui/how-to-combine-core-data-and-swiftui) about Core Data in SwiftUI. The Entity name is Student. And it has two properties: name (String), id (UUID). And the following is my code.
import SwiftUI
struct CoreView: View {
@Environment(\.managedObjectContext) var managedObject
@FetchRequest(sortDescriptors: []) var students: FetchedResults<Student>
var body: some View {
VStack {
List(students) { student in
Text(student.name ?? "Unknown")
}
Button {
let firstNames = ["Gary", "Harry", "Elane", "Ray", "Nancy", "Jim", "Susan"]
let lastNames = ["Johns", "McNamara", "Potter", "Thompson", "Hampton"]
if let selectedFirstName = firstNames.randomElement(), let selectedLastName = lastNames.randomElement() {
let newStudent = Student(context: managedObject)
newStudent.id = UUID()
newStudent.name = "\(selectedFirstName) \(selectedLastName)"
try? managedObject.save()
}
} label: {
Text("Add")
}
}
}
}
struct CoreView_Previews: PreviewProvider {
static var previews: some View {
CoreView()
.environmentObject(DataController())
}
}
If I list all records and then add a new student to the list, the app will insert the last addition at a random row. I wonder if I can order these records by the creation date?
Muchos thankos
Let me say that I have three structs that are sequentially connected.
ContentView -> FirstView -> SecondView
And I want to make a call from SecondView to ContentView with a button tap. So I have the following lines of code.
import SwiftUI
struct ContentView: View {
@State var goToFirst = false
var body: some View {
NavigationStack {
VStack {
NavigationLink {
FirstView(callBack: {
sayHello()
}, goToSecond: $goToFirst)
} label: {
Text("Go to First")
}
}
}
.navigationDestination(isPresented: $goToFirst) {
}
}
func sayHello() {
print("Hello!")
}
}
struct FirstView: View {
@State var callBack: (() -> Void)?
@Binding var goToSecond: Bool
var body: some View {
VStack {
Button("Go to Second") {
goToSecond.toggle()
}
}
.navigationDestination(isPresented: $goToSecond) {
SecondView(callBack: callBack)
}
}
}
struct SecondView: View {
@State var callBack: (() -> Void)?
var body: some View {
VStack {
Button("Tap me to make a call to ContentView") {
callBack?()
}
}
}
}
If I tap the button in SecondView, my ContentView will receive a call and call the sayHello function. Since ContentView and SecondView are not directly connected with each other, they have to through FirstView in this case. I wonder if there's a better or easier approach in having SecondView make a call to ContentView? In UIKit and Cocoa, you can make a delegate call to a distant class even when two classes are not directly connected with other. Using the notification is another option. In SwiftUI, I suppose you don't use either of them. Muchos thankos.
I have a sample macOS app that I'm working on. I can run the exactly same lines of code below for iOS. For now, I'm running code for macOS since I can just press Command + z to undo the last action.
Anyway, I have two Text View objects. Since TextView has the DragGesture gesture, I am able to freely move either of them. And I want to undo and redo their positions. So the following is what I have.
import SwiftUI
struct ContentView: View {
@State var textViews: [TextView] = [TextView(text: "George"), TextView(text: "Susan")]
var body: some View {
VStack {
ForEach(textViews, id: \.id) { textView in
textView
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct TextView: View {
@Environment(\.undoManager) var undoManager
@StateObject private var undoModel = UndoViewModel()
@State private var dragOffset: CGSize = .zero
@State private var position: CGSize = .zero
let id = UUID()
let text: String
init(text: String) {
self.text = text
}
var body: some View {
ZStack {
Text(text)
.fixedSize()
.padding(.vertical, 10)
.offset(x: dragOffset.width + position.width, y: dragOffset.height + position.height)
.gesture(
DragGesture()
.onChanged {
self.dragOffset = $0.translation
}
.onEnded( { (value) in
self.position.width += value.translation.width
self.position.height += value.translation.height
self.dragOffset = .zero
undoModel.registerUndo(CGSize(width: position.width, height: position.height), in: undoManager)
})
)
}
}
}
class UndoViewModel: ObservableObject {
@Published var point = CGSize.zero
func registerUndo(_ newValue: CGSize, in undoManager: UndoManager?) {
let oldValue = point
undoManager?.registerUndo(withTarget: self) { [weak undoManager] target in
target.point = oldValue // registers an undo operation to revert to old text
target.registerUndo(oldValue, in: undoManager) // this makes redo possible
}
undoManager?.setActionName("Move")
point = newValue // update the actual value
}
}
Well, if I press Command + z after moving one of them, it won't return to the last position. What am I doing wrong? Muchos thankos.
I'm trying to change the locale of an app with Picker as follows.
import SwiftUI
@main
struct LocaleSwitchCrazyMamaApp: App {
var body: some Scene {
WindowGroup {
let lanSetting = LanguageSetting()
ContentView()
.environmentObject(lanSetting)
.environment(\.locale, lanSetting.locale)
}
}
}
class LanguageSetting: ObservableObject {
@Published var locale = Locale(identifier: "en")
}
import SwiftUI
struct ContentView: View {
@State private var segmentSelection = 0
@EnvironmentObject var languageSetting: LanguageSetting
var body: some View {
VStack {
Text(NSLocalizedString("Hello", comment: ""))
.padding(.vertical, 20)
Picker("Language", selection: $segmentSelection) {
Text("English").tag(0)
Text("Japanese").tag(1)
Text("French").tag(2)
}
.frame(width: 200)
.pickerStyle(.segmented)
.onChange(of: segmentSelection) {newValue in
if newValue == 0 {
languageSetting.locale = Locale(identifier: "en")
} else if newValue == 1 {
languageSetting.locale = Locale(identifier: "ja")
} else {
languageSetting.locale = Locale(identifier: "fr")
}
}
}
.padding()
}
}
In addition, I have three locale versions like the following
"Hello" = "Hello"; // en.lproj
"Hello" = "Bonjour"; //fr.lproj
"Hello" = "こんにちは"; // ja.lproj
As long as I run the app on a simulator, the language of the Hello text won't change when tap any of the segments. What am I doing wrong?
Muchos thankos
I have a test app that is supposed to list a bunch of iTunes music records. I have existing lines of code that successfully load data. Now, I'm adding a loading state to them. As a result, I have the following lines code.
import SwiftUI
struct iTunesView: View {
@StateObject var viewModel = iTunesViewModel()
var body: some View {
switch viewModel.state {
case .idle: EmptyView()
case .loading: ProgressView()
case .loaded(let results):
List {
ForEach(results, id: \.self) { result in
Text("\(result.trackId)")
Text(result.trackName)
.lineLimit(0)
}
}
case .failed(let error):
Text(error.localizedDescription)
}
}
}
@MainActor
class iTunesViewModel: ObservableObject {
enum LoadingState {
case idle
case loading
case loaded([iTunesResult])
case failed(Error)
}
@Published var state: LoadingState = .idle
init() {
state = .loading
Task {
await fetchMusic()
}
}
func fetchMusic() async {
guard let url = URL(string: "https://itunes.apple.com/search?term=classical+music&entity=song") else {
state = .failed(URLError(.badURL))
return
}
do {
let urlRequest = URLRequest(url: url, timeoutInterval: 1.0)
let (data, _) = try await URLSession.shared.data(for: urlRequest)
let music = try JSONDecoder().decode(iTunesResponse.self, from: data)
self.state = .loaded(music.results)
print("\(music.results)")
} catch {
state = .failed(error)
}
}
}
struct iTunesResponse: Codable {
let resultCount: Int
let results: [iTunesResult]
}
struct iTunesResult: Codable, Hashable {
var trackId: Int
var trackName: String
var collectionName: String
}
enum iTunesError: Error {
case badURL
case decoding
case invalidHTTPResponse
case badData(statusCode: Int)
case badRequest(statusCode: Int)
case redirection(statusCode: Int)
case server(statusCode: Int)
case error(String)
}
For some reason, the app just shows a spinner although it reaches the print line in the fetchMusci function and print the string data. I wonder what I'm doing wrong? With the code lines above, an app is fully functional except it will show the progress guy, you know? I've made changes after reading this Stack overflow topic. Thanks.
I'm just playing with Combine, and I want to limit the number of letters that the text field can show. So I have the following lines of code.
import SwiftUI
struct ContentView: View {
@State var messageText: String = ""
var body: some View {
let bindingMessage = Binding {
messageText
} set: {
messageText = String($0.prefix(10))
print(String($0.prefix(10)))
}
Form {
TextField("Message", text: bindingMessage)
}
}
}
I'm expecting that the text field will show only the first 10 letters. But, as the screenshot below indicates, it shows the entire string. What am I doing wrong? Muchos Thankos. It sounds basic. Ugghhh...
I am perplexed as to how to use async await. In the following example, I don't use GCD or performSelector(inBackground:with:). The view controller is NSViewController, but it doesn't make any difference if it's NSViewController or UIViewController.
import Cocoa
class ViewController: NSViewController {
func startWriteImages() {
Task{
let bool = await startWriteImagesNext()
if bool {
print("I'm done!")
}
}
}
func startWriteImagesNext() async -> Bool {
// pictures is a path to a folder in the sandbox folder
// appDelegate.defaultFileManager is a variable pointing to FileManager.default in AppDelegate
let pictURL = URL(fileURLWithPath: pictures)
if let filePaths = try? self.appDelegate.defaultFileManager.contentsOfDirectory(atPath: pictURL.path) {
for file in filePaths {
let fileURL = pictURL.appending(component: file)
if self.appDelegate.defaultFileManager.fileExists(atPath: fileURL.path) {
let newURL = self.folderURL.appending(component: file)
do {
try self.appDelegate.defaultFileManager.copyItem(at: fileURL, to: newURL)
} catch {
print("Ugghhh...")
}
}
}
return true
}
return false
}
func startWriteImagesNext2() async -> Bool {
let pictURL = URL(fileURLWithPath: pictures)
if let filePaths = try? self.appDelegate.defaultFileManager.contentsOfDirectory(atPath: pictURL.path) {
DispatchQueue.global().async() {
for file in filePaths {
let fileURL = pictURL.appending(component: file)
if self.appDelegate.defaultFileManager.fileExists(atPath: fileURL.path) {
let newURL = self.folderURL.appending(component: file)
do {
try self.appDelegate.defaultFileManager.copyItem(at: fileURL, to: newURL)
} catch {
print("Ugghhh...")
}
}
}
}
return true
}
return false
}
}
In the code above, I'm saving each file in the folder to user-selected folder (self.folderURL). And the application will execute the print guy only when work is done. Since it's heavy-duty work, I want to use CCD or performSelector(inBackground:with:). If I use the former (startWriteImagesNext2), the application will execute the print guy right at the beginning. I suppose I cannot use GCD with async. So how can I perform heavy-duty work? Muchos thankos.
A device running with the following lines of code can receive a message from a peripheral. In this manner, though, I can only receive messages from one peripheral since the service and characteristic IDs are hardcoded in CentralViewModel.swift. So my question is how I can observe messages from multiple peripherals. Thanks.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
NavigationLink(destination: CentralView()) {
Text("Central")
}
.buttonStyle(.borderedProminent)
.padding()
}
}
}
}
// CentralView.swift //
import SwiftUI
struct CentralView: View {
@StateObject var central: CentralViewModel = CentralViewModel()
var body: some View {
Text(central.message)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding(20)
.onDisappear {
central.stopAction()
}
}
}
// CentralViewModel.swift //
import Foundation
import CoreBluetooth
class CentralViewModel: NSObject, ObservableObject {
@Published var message: String = ""
var serviceUUID: CBUUID!
var characteristicUUID: CBUUID!
var centralManager: CBCentralManager!
var discoveredPeripheral: CBPeripheral?
var transferCharacteristic: CBCharacteristic?
var writeIterationsComplete = 0
//var connectionIterationsComplete = 0
let defaultIterations = 5
var data: Data = Data()
override init() {
super.init()
self.serviceUUID = CBUUID(string: "994F8A12-FE8E-4CCB-BD7B-1AE989A32853")
self.characteristicUUID = CBUUID(string: "F4BD0CA2-7581-40E2-A517-1CE275A3A749")
centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey: true])
}
func stopAction() {
centralManager.stopScan()
}
private func cleanup() {
guard let discoveredPeripheral = discoveredPeripheral, case .connected = discoveredPeripheral.state else { return }
for service in (discoveredPeripheral.services ?? [] as [CBService]) {
for characteristic in (service.characteristics ?? [] as [CBCharacteristic]) {
if characteristic.uuid == characteristicUUID && characteristic.isNotifying {
self.discoveredPeripheral?.setNotifyValue(false, for: characteristic)
}
}
}
centralManager.cancelPeripheralConnection(discoveredPeripheral)
}
private func writeData() {
guard let discoveredPeripheral = discoveredPeripheral, let transferCharacteristic = transferCharacteristic
else {
return
}
while writeIterationsComplete < defaultIterations && discoveredPeripheral.canSendWriteWithoutResponse {
writeIterationsComplete += 1
}
if writeIterationsComplete == defaultIterations {
discoveredPeripheral.setNotifyValue(false, for: transferCharacteristic)
}
}
}
extension CentralViewModel: CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("Power on")
startScanningForPeripherals()
return
case .poweredOff :
print("Power off")
return
case .resetting:
print("Resetting")
return
case .unauthorized:
print("Unauthorized")
return
case .unknown:
print("Unknown")
return
case .unsupported:
print("Unsupported")
return
@unknown default:
print("An unknown central manager state has occurred")
return
}
}
func startScanningForPeripherals() {
self.centralManager.scanForPeripherals(withServices: [self.serviceUUID], options: nil)
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard RSSI.intValue >= -50 else {
return
}
if discoveredPeripheral != peripheral {
print("Peripheral discovered")
discoveredPeripheral = peripheral
centralManager.connect(peripheral, options: nil)
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices([serviceUUID])
print("Service discovered")
}
}
extension CentralViewModel: CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if error != nil {
cleanup()
return
}
guard let peripheralServices = peripheral.services else {
return
}
for service in peripheralServices {
peripheral.discoverCharacteristics([characteristicUUID], for: service)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let error = error {
print("Error discovering characteristics: \(error.localizedDescription)")
cleanup()
return
}
guard let serviceCharacteristics = service.characteristics else {
return
}
for characteristic in serviceCharacteristics where characteristic.uuid == characteristicUUID {
transferCharacteristic = characteristic
peripheral.setNotifyValue(true, for: characteristic)
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("Error changing notification state: \(error.localizedDescription)")
return
}
guard characteristic.uuid == characteristicUUID else {
return
}
if characteristic.isNotifying {
print("Notification began on \(characteristic)")
} else {
print("Notification stopped on \(characteristic). Disconnecting")
cleanup()
}
}
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
print("Peripheral is ready to send data to YOU!")
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("Error discovering characteristics: \(error.localizedDescription)")
cleanup()
return
}
guard let characteristicData = characteristic.value,
let stringFromData = String(data: characteristicData, encoding: .utf8) else {
return
}
print("Received \(characteristicData.count) bytes: \(stringFromData)")
if stringFromData == "EOM" {
message = String(data: data, encoding: .utf8) ?? ""
writeData()
} else {
data.append(characteristicData)
}
}
}
I have a simple SwiftUI project with two basic build configurations (Debug, Release) as shown below.
I now choose Build > Scheme > Edit Scheme under Product and select Release as the current build configuration as shown below.
And the Preview canvas exhibit errors.
If I click on the Diagnostics button, it says under PREVIEW UPDATE ERROR
OptimizationLevelError: not building -Onone
”BuildSchemeCrazyDaughter.app” needs -Onone Swift optimization level to use previews (current setting is -O)
What does that mean and why don't I get the preview for the Release build configuration? Thanks.
I have a macOS application developed in SwiftUI. It's a document-based application. I know how to hide the Show Tab Bar command under View. I don't want to hide it. I always want to show tabs. I wonder how to enable this command programmatically such that the document window always has the + button to the right. Thanks.
The other day I was playing with iBeacon and found out that CLBeaconIdentityConstraint will be deprecated after iOS 18.5. So I've written code with BeaconIdentityCondition in reference to this Apple's sample project.
import Foundation
import CoreLocation
let monitorName = "BeaconMonitor"
@MainActor
public class BeaconViewModel: ObservableObject {
private let manager: CLLocationManager
static let shared = BeaconViewModel()
public var monitor: CLMonitor?
@Published var UIRows: [String: [CLMonitor.Event]] = [:]
init() {
self.manager = CLLocationManager()
self.manager.requestWhenInUseAuthorization()
}
func startMonitoringConditions() {
Task {
print("Set up monitor")
monitor = await CLMonitor(monitorName)
await monitor!.add(getBeaconIdentityCondition(), identifier: "TestBeacon")
for identifier in await monitor!.identifiers {
guard let lastEvent = await monitor!.record(for: identifier)?.lastEvent else { continue }
UIRows[identifier] = [lastEvent]
}
for try await event in await monitor!.events {
guard let lastEvent = await monitor!.record(for: event.identifier)?.lastEvent else { continue }
if event.state == lastEvent.state {
continue
}
UIRows[event.identifier] = [event]
UIRows[event.identifier]?.append(lastEvent)
}
}
}
func updateRecords() async {
UIRows = [:]
for identifier in await monitor?.identifiers ?? [] {
guard let lastEvent = await monitor!.record(for: identifier)?.lastEvent else { continue }
UIRows[identifier] = [lastEvent]
}
}
func getBeaconIdentityCondition() -> CLMonitor.BeaconIdentityCondition {
CLMonitor.BeaconIdentityCondition(uuid: UUID(uuidString: "abc")!, major: 123, minor: 789)
}
}
It works except that my sample app can take as long as 90 seconds to see event changes. You would get an instant update with an fashion (CLBeacon and CLBeaconIdentityConstraint). Is there anything that I can do to see changes faster? Thanks.