CBCentralManager *manager EXC_BAD_ACCESS with iOS7.0 - cbcentralmanager

I just upgraded to Xcode V5.0 (5A1413) the build success but running the program against the emulator causes the error at the property definition:
#property (nonatomic, strong) CBCentralManager *manager; --> Thread 1:EXC_BAD_ACCESS (code=2, address=0x8)

I ran into the same issue and finally resorted to this:
UIDevice *currentDevice = [UIDevice currentDevice];
if ([currentDevice.model rangeOfString:#"Simulator"].location == NSNotFound) {
self.centralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
On the Simulator, if I don't guard against creation of the CBCentralManager, I see centralManagerDidUpdateState: called with a CBCentralManager* that matches my strong property. It can be referenced and the state is CBCentralManagerStateUnsupported. That makes sense, but if I nil my strong manager property at that point (since I'm not going to be doing any BLE on a simulator that doesn't support it) I get the EXC_BAD_ACCESS. So, in the absence of a better answer, I suggest you simply guard against firing up the manager at all, as in my code above.

Related

BackoffExceptions are logged at error level when using RetryTopicConfiguration

I am a happy user of the recently added RetryTopicConfiguration there is however a small issue that is bothering me.
The setup I use looks like:
#Bean
public RetryTopicConfiguration retryTopicConfiguration(
KafkaTemplate<String, String> template,
#Value("${kafka.topic.in}") String topicToInclude,
#Value("${spring.application.name}") String appName) {
return RetryTopicConfigurationBuilder
.newInstance()
.fixedBackOff(5000L)
.maxAttempts(3)
.retryTopicSuffix("-" + appName + ".retry")
.suffixTopicsWithIndexValues()
.dltSuffix("-" + appName + ".dlq")
.includeTopic(topicToInclude)
.dltHandlerMethod(KAFKA_EVENT_LISTENER, "handleDltEvent")
.create(template);
}
When the a listener throws an exception that triggers a retry, the DefaultErrorHandler will log a KafkaBackoffException at error level.
For a similar problem it was suggested to use a ListenerContainerFactoryConfigurer yet this does not remove all error logs, since I still see the following in my logs:
2022-04-02 17:34:33.340 ERROR 8054 --- [e.retry-0-0-C-1] o.s.kafka.listener.DefaultErrorHandler : Recovery of record (topic-spring-kafka-logging-issue.retry-0-0#0) failed
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener failed; nested exception is org.springframework.kafka.listener.KafkaBackoffException: Partition 0 from topic topic-spring-kafka-logging-issue.retry-0 is not ready for consumption, backing off for approx. 4468 millis.
Can the log-level be changed, without adding a custom ErrorHandler?
Spring-Boot version: 2.6.6
Spring-Kafka version: 2.8.4
JDK version: 11
Sample project: here
Thanks for such a complete question. This is a known issue of Spring for Apache Kafka 2.8.4 due to the new combine blocking and non-blocking exceptions feature and has been fixed for 2.8.5.
The workaround is to clear the blocking exceptions mechanism such as:
#Bean(name = RetryTopicInternalBeanNames.LISTENER_CONTAINER_FACTORY_CONFIGURER_NAME)
public ListenerContainerFactoryConfigurer lcfc(KafkaConsumerBackoffManager kafkaConsumerBackoffManager,
DeadLetterPublishingRecovererFactory deadLetterPublishingRecovererFactory,
#Qualifier(RetryTopicInternalBeanNames
.INTERNAL_BACKOFF_CLOCK_BEAN_NAME) Clock clock) {
ListenerContainerFactoryConfigurer lcfc = new ListenerContainerFactoryConfigurer(kafkaConsumerBackoffManager, deadLetterPublishingRecovererFactory, clock);
lcfc.setBlockingRetriesBackOff(new FixedBackOff(0, 0));
lcfc.setErrorHandlerCustomizer(eh -> ((DefaultErrorHandler) eh).setClassifications(Collections.emptyMap(), true));
return lcfc;
}
Please let me know if that works for you.
Thanks.
EDIT:
This workaround disables only blocking retries, which since 2.8.4 can be used along non-blocking as per the link in the original answer. The exception classification for the non-blocking retries is in the DefaultDestinationTopicResolver class, and you can set FATAL exceptions as documented here.
EDIT: Alternatively, you can use the Spring Kafka 2.8.5-SNAPSHOT version by adding the Spring Snapshot repository such as:
repositories {
maven {
url 'https://repo.spring.io/snapshot'
}
}
dependencies {
implementation 'org.springframework.kafka:spring-kafka:2.8.5-SNAPSHOT'
}
You can also downgrade to Spring Kafka 2.8.3.
As Gary Russell pointed out, if your application is already in production you should not use the SNAPSHOT version, and 2.8.5 is out in a couple of weeks.
EDIT 2: Glad to hear you’re happy about the feature!

How do I update Xcode codes I can use iOS 14 functions?

I was watching a tutorial on how to fetch user location in swift and I had a problem here:
class teste: CLLocationManager, CLLocationManagerDelegate{
#Published var lctionManager = CLLocationManager()
func locationManagerDidChangeAuthorization (_ manager: CLLocationManagerDelegate){
switch manager.authorizationStatus {
case .authorizedWhenInUse:
print("authorized")
case .denied:
print("denied")
default:
print("unkown")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error){
print(error.localizedDescription)
}
}
The error was a in locationManagerDiChangeAuthorization (Instance method 'locationManagerDidChangeAuthorization' nearly matches optional requirement 'locationManagerDidChangeAuthorizantion' of protocol 'locationManagerDelegate') and in manager.authorizationStatus( Value of type 'CLLocationManagerDelegate' has no member 'authoizationStatus')
After some research, I found out that these are iOS 14 only, and my code may be written in iOS13 (actually, for some codes, I have to add #available(iOS 14.0, *) to make them work, but this time it didnt seem it work).
But, as a beginner, I don't know how to update my code (searched for some stuff but nothing caught my eyes). How do I update my code? Would it interfere in anything? Is it necessary or its better to write something to integrate both iOS 14 and 13?
Project -> Info -> iOS Deployment Target
here you can change deployment target to iOS 14.

Scheduling complication updates

I have a custom complication on Apple Watch that I am trying to get to update once an hour. Each hour it should ping an API endpoint and if the data has changed from the last check, the complication should be updated.
Here is what I currently have that only seems to work once in a blue moon. When it DOES work, it does indeed ping my server and update the complication. It appears WatchOS just isn't calling my scheduled tasked once per hour. Is there a better standard practice that I'm missing?
#implementation ExtensionDelegate
- (void)applicationDidFinishLaunching {
// Perform any final initialization of your application.
[SessionManager sharedManager];
[self scheduleHourlyUpdate];
}
- (void) scheduleHourlyUpdate {
NSDate *nextHour = [[NSDate date] dateByAddingTimeInterval:(60 * 60)];
NSDateComponents *dateComponents = [[NSCalendar currentCalendar]
components: NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:nextHour];
[[WKExtension sharedExtension] scheduleBackgroundRefreshWithPreferredDate:nextHour userInfo:nil scheduledCompletion:^(NSError * _Nullable error) {
// schedule another one in the next hour
if (error != nil)
NSLog(#"Error while scheduling background refresh task: %#", error.localizedDescription);
}];
}
- (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks {
// Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one.
for (WKRefreshBackgroundTask * task in backgroundTasks) {
// Check the Class of each task to decide how to process it
if ([task isKindOfClass:[WKApplicationRefreshBackgroundTask class]]) {
// Be sure to complete the background task once you’re done.
WKApplicationRefreshBackgroundTask *backgroundTask = (WKApplicationRefreshBackgroundTask*)task;
[backgroundTask setTaskCompletedWithSnapshot:NO];
[self updateComplicationServer];
} else if ([task isKindOfClass:[WKSnapshotRefreshBackgroundTask class]]) {
// Snapshot tasks have a unique completion call, make sure to set your expiration date
WKSnapshotRefreshBackgroundTask *snapshotTask = (WKSnapshotRefreshBackgroundTask*)task;
[snapshotTask setTaskCompletedWithDefaultStateRestored:YES estimatedSnapshotExpiration:[NSDate distantFuture] userInfo:nil];
} else if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) {
// Be sure to complete the background task once you’re done.
WKWatchConnectivityRefreshBackgroundTask *backgroundTask = (WKWatchConnectivityRefreshBackgroundTask*)task;
[backgroundTask setTaskCompletedWithSnapshot:NO];
} else if ([task isKindOfClass:[WKURLSessionRefreshBackgroundTask class]]) {
// Be sure to complete the background task once you’re done.
WKURLSessionRefreshBackgroundTask *backgroundTask = (WKURLSessionRefreshBackgroundTask*)task;
[backgroundTask setTaskCompletedWithSnapshot:NO];
} else if ([task isKindOfClass:[WKRelevantShortcutRefreshBackgroundTask class]]) {
// Be sure to complete the relevant-shortcut task once you’re done.
WKRelevantShortcutRefreshBackgroundTask *relevantShortcutTask = (WKRelevantShortcutRefreshBackgroundTask*)task;
[relevantShortcutTask setTaskCompletedWithSnapshot:NO];
} else if ([task isKindOfClass:[WKIntentDidRunRefreshBackgroundTask class]]) {
// Be sure to complete the intent-did-run task once you’re done.
WKIntentDidRunRefreshBackgroundTask *intentDidRunTask = (WKIntentDidRunRefreshBackgroundTask*)task;
[intentDidRunTask setTaskCompletedWithSnapshot:NO];
} else {
// make sure to complete unhandled task types
[task setTaskCompletedWithSnapshot:NO];
}
}
}
- (void)updateComplicationServer {
[self scheduleHourlyUpdate];
NSString *nsLogin = [NSUserDefaults.standardUserDefaults objectForKey:#"loginDTO"];
if (nsLogin != nil)
{
NSDateComponents *dateComponents = [[NSCalendar currentCalendar]
components: NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
LoginDTO *login = new LoginDTO([nsLogin cStringUsingEncoding:NSUTF8StringEncoding]);
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://www.myurl.com/Api/Watch/Complication"]];
[req setHTTPMethod:#"GET"];
// Set headers
[req addValue:[NSString stringWithUTF8String:login->GetApiKey()] forHTTPHeaderField:#"MySessionKey"];
[req addValue:[NSString stringWithFormat:#"%d,%d,%d", dateComponents.year, dateComponents.month, dateComponents.day] forHTTPHeaderField:#"FetchDate"];
[req addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest:req completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
{
// Call is complete and data has been received
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if (httpResponse.statusCode == 200)
{
NSString* nsJson = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *prevJson = [NSUserDefaults.standardUserDefaults objectForKey:#"previousComplicationJson"];
if (prevComplicationJson != nil)
{
if ([prevComplicationJson isEqualToString:nsJson])
return; // Nothing changed, so don't update the UI.
}
// Update the dictionary
[NSUserDefaults.standardUserDefaults setObject:nsJson forKey:#"previousComplicationJson"];
CLKComplicationServer *server = [CLKComplicationServer sharedInstance];
for (int i = 0; i < server.activeComplications.count; i++)
[server reloadTimelineForComplication:server.activeComplications[i]];
}
}];
[task resume];
delete login;
}
}
watchOS background tasks are extraordinarily cumbersome to implement and debug, but based on Apple’s documentation and implementations that others have discussed, here‘s what I believe to be the best practice. I see a couple of issues here.
First, from the WKRefreshBackgroundTask docs:
The system suspends the extension as soon as all background tasks are complete.
Calling setTaskCompletedWithSnapshot on the task indicates to the system that you’ve finished performing all the work you need to do, so it will suspend your app. Your updateComplicationServer method is probably never getting a chance to run because the system suspends your extension too early.
More importantly, to make URL requests during a background update, you’ll need to use a background URL session. The example process outlined in the WKRefreshBackgroundTask docs shows the best practice for setting this up. In short:
You schedule a background refresh using WKExtension’s scheduleBackgroundRefresh just like you’re doing.
The system will wake your extension sometime after your preferred date (at the system’s discretion) with a WKRefreshBackgroundTask.
In your extension delegate’s handle method, check for the WKApplicationRefreshBackgroundTask; instead of performing the request with a URLSessionDataTask here, you need to schedule a background URL session so that the system can suspend your extension and perform the request on your behalf. See the WKURLSessionRefreshBackgroundTask docs for details about how background sessions should be set up.
The system will perform your URL request in a separate process, and again wake your extension once it has finished. It will call your extension delegate’s handle method like before, this time with a WKURLSessionRefreshBackgroundTask. Here, you need to do two things:
Save the background task in an instance variable on your extension delegate. We don’t want to set it complete just yet, but we need to keep it around to set complete later when the URL request finishes.
Create another background URL session using the background task’s sessionIdentifier, and use your extension delegate as the session’s delegate (why it doesn’t work to use another object as the delegate, I can’t say, but it seems to be a crucial detail). Note that using the same identifier to create a second URL session allows the system to connect the session to the download that it performed for you in another process; the purpose of this second background URL session is solely to connect the delegate with the session.
In the session delegate, implement both the urlSession(_ downloadTask: didFinishDownloadingTo:) and urlSession(task: didCompleteWithError:) functions.
Unlike your block-based NSURLSessionDataTask, background URL requests are always performed as download tasks. The system performs the request and gives you a temporary file with the resulting data. In the urlSession(_ downloadTask: didFinishDownloadingTo:) function, the data in that file and process it as needed to update your UI.
Finally, in the delegate’s urlSession(task: didCompleteWithError:) function, call setTaskCompletedWithSnapshot to tell the system that you’ve finished your work. Phew.
As I mentioned, this is all really frustrating to debug, mostly because it’s all up to the system when these things actually take place, if they happen at all. Apple’s documentation has this to say about the budget allocated to background refreshes:
In general, the system performs approximately one task per hour for each app in the dock (including the most recently used app). This budget is shared among all apps on the dock. The system performs multiple tasks an hour for each app with a complication on the active watch face. This budget is shared among all complications on the watch face. After you exhaust the budget, the system delays your requests until more time becomes available.
One final note: legend has it that the watchOS simulator doesn’t handle background URL refresh tasks properly, but unfortunately, Apple’s docs have no official word on that. Best to test on Apple Watch hardware if you‘re able.

How to start Go's main function from within the NSApplication event loop?

I'm trying to add Sparkle into my Qt (binding for Go) app to make it can be updated automatically.
Problem: there is no popup dialog when running the latest version
Here's the code: https://github.com/sparkle-project/Sparkle/blob/master/Sparkle/SUUIBasedUpdateDriver.m#L104
The reason as the author pointed out is NSAlert needs a run loop to work.
I found some docs:
https://wiki.qt.io/Application_Start-up_Patterns
https://developer.apple.com/documentation/appkit/nsapplication
So, as I understand, we have to instantiate NSApplication before creating a QApplication.
void NSApplicationMain(int argc, char *argv[]) {
[NSApplication sharedApplication];
[NSBundle loadNibNamed:#"myMain" owner:NSApp];
[NSApp run];
}
My Go's main function is something like this:
func main() {
widgets.NewQApplication(len(os.Args), os.Args)
...
action := widgets.NewQMenuBar(nil).AddMenu2("").AddAction("Check for Updates...")
// http://doc.qt.io/qt-5/qaction.html#MenuRole-enum
action.SetMenuRole(widgets.QAction__ApplicationSpecificRole)
action.ConnectTriggered(func(bool) { sparkle_checkUpdates() })
...
widgets.QApplication_Exec()
}
Question: how can I start Go's main function from within the NSApplicationMain event loop?
Using QApplication together with a Runloop
Regarding your question how to use your QApplication together with a NSRunloop: you are doing it already.
Since you are using QApplication (and not QCoreApplication) you already have a Runloop running,
see http://code.qt.io/cgit/qt/qt.git/plain/src/gui/kernel/qeventdispatcher_mac.mm and http://code.qt.io/cgit/qt/qt.git/plain/src/plugins/platforms/cocoa/qcocoaeventloopintegration.mm
Proof
A NSTimer needs a run loop to work. So we could add quick test with an existing example Qt app called 'widget' from the repository you referenced in your question.
Adding a small Objective-C test class TimerRunloopTest with a C function wrapper that can be called from GO:
#import <Foundation/Foundation.h>
#include <os/log.h>
#interface TimerRunloopTest : NSObject
- (void)run;
#end
void runTimerRunloopTest() {
[[TimerRunloopTest new] run];
}
#implementation TimerRunloopTest
- (void)run {
os_log_t log = os_log_create("widget.example", "RunloopTest");
os_log(log, "setup happening at %f", NSDate.timeIntervalSinceReferenceDate);
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerTick:)
userInfo:nil
repeats:YES];
}
- (void)timerTick:(NSTimer *)timer {
os_log_t log = os_log_create("widget.example", "RunloopTest");
os_log(log, "timer tick %f", NSDate.timeIntervalSinceReferenceDate);
}
#end
GO counterpart timerrunlooptest.go
package main
/*
#cgo LDFLAGS: -framework Foundation
void runTimerRunloopTest();
*/
import "C"
func runTimerRunloopTest() { C.runTimerRunloopTest() }
Change in main.go
At the end before app.Exec() add this line:
runTimerRunloopTest()
Build and Run it
Switch loggin on for our logging messages:
sudo log config --subsystem widget.example --mode level:debug
Afterwards build an run it:
$(go env GOPATH)/bin/qtdeploy test desktop examples/basic/widgets
Test
In the macOS Console uitlity we can now see, that the timer ticks are shown, proofing that a run-loop is running
NSAlert
Then you cited in your question, that NSAlert needs a run loop to work. We already proofed that we have one, but testing it explicitely makes sense.
So we can modify timerrunlooptest.go to inform it, that we want to link agains Cocoa also, not only Foundation:
package main
/*
#cgo LDFLAGS: -framework Foundation
#cgo LDFLAGS: -framework Cocoa
void runTimerRunloopTest();
*/
import "C"
func runTimerRunloopTest() { C.runTimerRunloopTest() }
Then we could add the following code to the run method of TimerRunLoopTest:
#import <Cocoa/Cocoa.h>
...
NSAlert *alert = [[NSAlert alloc] init];
alert.messageText = #"Message";
alert.informativeText = #"Info";
[alert addButtonWithTitle:#"OK"];
[alert runModal];
Result
After doing a
$(go env GOPATH)/bin/qtdeploy test desktop examples/basic/widgets
the native Alert is shown from the GO/QT application as expected:
Mixing Qt with Native Code
Although we seem to be able to display native alerts in the way described above, there is this hint in the QT documents that may or may not be useful:
Qt's event dispatcher is more flexible than what Cocoa offers, and lets the user spin the event dispatcher (and running QEventLoop::exec) without having to think about whether or not modal dialogs are showing on screen (which is a difference compared to Cocoa). Therefore, we need to do extra management in Qt to handle this correctly, which unfortunately makes mixing native panels hard. The best way at the moment to do this, is to follow the pattern below, where we post the call to the function with native code rather than calling it directly. Then we know that Qt has cleanly updated any pending event loop recursions before the native panel is shown.
see https://doc.qt.io/qt-5/macos-issues.html#using-native-cocoa-panels
There is also a small code example for this.

Doctrine setAutoCommit(false) method and "there is no active transaction" message

EDIT: The question is about why using setAutoCommit(false) is a solution for the "there is no active transaction" exception. Forget this question since this is not the correct solution (at least in my case). I will leave the question here in case somebody encounters the same problem. See my answer below for more details.
================
The following code worked fine in Symfony 2.7, but after an update to Symfony 2.8 (and to the latest DoctrineBundle version), a There is no active transaction exception is thrown:
private function getSynchronization() {
$lock_repo = $this->entityManager->getRepository('MyAppBundle\Entity\DBLock');
$this->entityManager->getConnection()->beginTransaction();
try {
$sync = $lock_repo->findOneByUser($this->getUser());
if (!$lock) {
$lock = new DBLock();
} else {
if ($lock->isActive()) {
// ... Exception: Process already running
}
$expected_version = $lock->getVersion();
$this->entityManager->lock($lock, LockMode::OPTIMISTIC, $expected_version);
}
$sync->setActive(false);
$this->entityManager->persist($sync);
$this->entityManager->flush();
$this->entityManager->getConnection()->commit();
// EXCEPTION on this line
$this->entityManager->lock($lock, LockMode::NONE);
}
catch(\Exception $e) {
$this->entityManager->getConnection()->rollback();
throw new ProcessException($e->getMessage());
}
...
}
After some searching I found a solution in another post. After adding the following line everything works fine:
private function getSynchronization() {
$lock_repo = $this->entityManager->getRepository('MyAppBundle\Entity\DBLock');
$this->entityManager->getConnection()->beginTransaction();
// ADDED LINE
$this->entityManager->getConnection()->setAutoCommit(false);
try {
...
So, the question is not how to solve the problem, but how the solution works...
I am quite confused by the Doctrine docs of the setAutoCommit() method:
To have a connection automatically open up a new transaction on
connect() and after commit() or rollBack(), you can disable
auto-commit mode with setAutoCommit(false)
I do not understand this.
Does this mean, that the transaction that was started/created with beginTransaction() is now automatically closed when using commit()? So in order to be able to use lock(...) after using commit() I have to begin a new transaction first. I can either do this manually by calling beginTransaction() again, or auotmatically by setting setAutoCommit(false) before. Is that correct?
Is this a change in on of the latest Doctrine versions? I did not found anything about in in the updates notes and before the update of Symfony/Doctrine the code worked just fine.
Thank you very much!
As described before I encountered the problem, that calling lock($lock, LockMode::NONE) suddenly threw a There is no active transaction exception after the Update from Doctrine 2.4 to 2.5.
My solution was to add setAutoCommit(false), which automatically created a new transaction after calling commit(). It worked and the exception did not occur again. However, this is not the real/correct solution, it creates other problems as side effects.
After re-reading the Doctrine Update Notes I found out, that the correct solution is to use lock($lock, null) instead of lock($lock, LockMode::NONE). This is BC Break between Doctrine 2.4 and 2.5.
Maybe my question and answer helps someone else who encounters the same problem.

Resources