Forum Archive

Dealing with a completion handler

mcriley821

I’m trying to make an notification system that will send a system notification when there’s a new topic on the most recent section of the forum. I’m trying to use the objc_util to schedule the function to the background threads. The function itself works, and sending the update to the background works, but the scheduler doesn’t repeat the function after the interval time. In: https://developer.apple.com/documentation/foundation/nsbackgroundactivityscheduler?language=objc
, it says that the scheduler won’t repeat if you don’t invoke the completion handler and send it the “all good flag” (integer 1).

I have no idea how to send this flag to the completion handler. I’ve been through plenty of crashes to no avail lol. Here’s the code:


import requests as r
from bs4 import BeautifulSoup as soup
import notification
from objc_util import *

def update(_cmd,completion_handler):#not sure on number of args or types
    with open('most_recent.txt','rb') as f:
        most_recent=f.read()

    print('checking') #debug
    page=r.get('https://forum.omz-software.com/recent')
    page=soup(page.content,'html.parser')
    newest=page.select('meta')[8]
    content_name=newest.attrs.get('content')
    if bytes(content_name,'utf8') != most_recent:
        print(content_name)#debug
        with open('most_recent.txt','wb') as f: 
            f.write(bytes(content_name,'utf8'))
        notification.schedule(message=f'Newest on the Pythonista Forum: {content_name}',action_url='https://forum.omz-software.com/recent',title='Pythonsita Forum')
    else:#debug
        notification.schedule(message='Nothing new...',title='Pythonista Forum')#debug
    #ObjCInstance(completion_handler).completion=1#doesnt work
    return

scheduler=ObjCClass("NSBackgroundActivityScheduler").alloc().initWithIdentifier_('com.omz-software.Pythonista3.update_forums')
scheduler.setInterval_(10)#more time when working
scheduler.setTolerance_(5)
scheduler.setRepeats_(True)
scheduler.setQualityOfService_(25)#may change when working 
#not sure on restype or argtypes either 
block=ObjCBlock(update,restype=None,argtypes=[c_void_p,c_void_p])
scheduler.scheduleWithBlock_(block)
cvp

@mcriley821 did you check _objc_exception.txt file?

mcriley821

@cvp No exception file is created usually. I forgot what I did one time, but I got an exception once, and it said wrong selector sent to instance, during the first repeat. I’ve tried a ton of things though. Returning 1; 1,2,3 args; Different argtypes; different restypes;
The current code is one of the few that will actually run, and I feel it’s the least convoluted.

cvp

@mcriley821 Is NSBackgroundActivityScheduler not only a MacOS object?

mcriley821

@cvp I just read that in the Apple docs, but how could I even create the class if that was true?

cvp

@mcriley821 I don't know, sorry. And strange because your block is called 🤔

mcriley821

Maybe @JonB has experienced this before?

mcriley821

Found a better solution @cvp by digging in @JonB objc_hacks.

import requests as r
from bs4 import BeautifulSoup as soup
from objc_util import *
import notification
import ctypes

class _block_descriptor (Structure):
  _fields_ = [('reserved', c_ulong), ('size', c_ulong), ('copy_helper', c_void_p), ('dispose_helper', c_void_p), ('signature', c_char_p)]
InvokeFuncType = ctypes.CFUNCTYPE(None, *[c_void_p, ctypes.c_int])
class _block_literal(Structure):
  _fields_ = [('isa', c_void_p), ('flags', c_int), ('reserved', c_int), ('invoke', InvokeFuncType), ('descriptor', _block_descriptor)]


def update_forum(_cmd,_comp):
    comp=ObjCInstance(_comp)
    blk=_block_literal.from_address(_comp)
    blk.invoke(comp,1)
    with open('most_recent.txt','rb') as f:
        lst=f.readlines()
        most_recent,count=lst[0][:-1],lst[1]
    url='https://forum.omz-software.com/recent'
    page=r.get(url)
    page=soup(page.content,'html.parser')
    newest=page.select('meta')[8]
    content_name=newest.attrs.get('content')
    content_name=bytes(content_name,'utf8')
    post_count=newest.find_all('span')[2].attrs.get('title')
    post_count=bytes(post_count,'utf8')

    if content_name != most_recent or post_count != count:
        with open('most_recent.txt','wb') as f: 
            f.writelines([content_name,
            b'\n',
            post_count])
        notification.schedule(
            message=f'Newest on the Forum: {content_name.decode()}',
            action_url=url,
            title='Pythonista Forum',
            identifier='Forum_Updater'
            )
    #else:
        #notification.schedule(
            message='Nothing new...',
            title='Pythonista Forum',
            identifier='Forum_Updater'
            )
    return

scheduler=ObjCClass('NSBackgroundActivityScheduler').alloc().initWithIdentifier_('Scheduler')
scheduler.setRepeats_(True)
scheduler.setInterval_(10*60)
scheduler.setTolerance_(60)
scheduler.setQualityOfService_(25)

block=ObjCBlock(update_forum,restype=None,argtypes=[c_void_p,c_void_p])
scheduler.scheduleWithBlock_(block) 

Sadly, it only works when in the app. It does free up the console though