Preamble: I am creating an iOS build of a Ren'Py game. My coding experience with Swift/ObjC is nearly nonexistent and I've primarily followed tutorials up to this point. Ren'Py uses an underlying framework to create an Xcode project. I do not have control over how this framework does things, but I can add files before actually compiling inside Xcode. I MUST use the pyobjus library to do so based on my current understanding, abilities, and frameworks. The included IAPHelper class processes in-app purchasing and it works great! The following function indicates, however, that there is a rootViewController that I need to attach to.
- (void) showDialog {
if (alert != nil) {
return;
}
alert = [UIAlertController
alertControllerWithTitle:self.dialogTitle
message:nil
preferredStyle:UIAlertControllerStyleAlert ];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
// Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(alert.view.bounds.size.width / 2, alert.view.bounds.size.height - 50);
[indicator startAnimating];
[alert.view addSubview: indicator];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
#pragma clang diagnostic pop
}
Problem: I am TRYING to implement an in-app browser for Patreon authentication purposes. The files I have put together are as follows:
BrowserViewController.h
// BrowserViewController.h
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@interface BrowserViewController : UIViewController
- (void)loadURL:(NSString *)urlString;
@end
BrowserViewController.m
// BrowserViewController.m
#import "BrowserViewController.h"
@interface BrowserViewController ()
@property (nonatomic, strong) WKWebView *webView;
@end
@implementation BrowserViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.webView];
NSLog(@"viewDidLoad");
}
- (void)loadURL:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
NSLog(@"loadURL");
}
@end
OAuth2Strategy.py
init python in auth:
from urllib.parse import urlencode, urlparse, parse_qs
from urllib.request import urlopen, Request
import ssl
import certifi
from store import webserver, OpenURL
import json
class OAuth2Strategy():
def __init__(self, authorization_url, token_url, client_id, client_secret, callback_url, scope):
self.authorization_url = authorization_url
self.token_url = token_url
self.client_id = client_id
self.client_secret = client_secret
self.callback_url = callback_url
self.scope = scope
self.on_success_callback = None
self.on_fail_callback = None
def run(self, on_success_callback = None, on_fail_callback = None):
self.on_success_callback = on_success_callback
self.on_fail_callback = on_fail_callback
webserver.start(self)
if renpy.renpy.ios:
from pyobjus import autoclass, objc_str
# Import the Objective-C class
BrowserViewController = autoclass('BrowserViewController')
# Create an instance of the BrowserViewController
browser = BrowserViewController.alloc().init()
# Load a URL
url = self.make_authorize_url()
browser.loadURL_(objc_str(url))
elif renpy.renpy.android:
pass
else:
renpy.run(OpenURL(self.make_authorize_url()))
def make_authorize_url(self):
return self.authorization_url + "?client_id={client_id}&scope={scope}&redirect_uri={redirect_uri}&response_type=code".format(
client_id=self.client_id,
scope=self.scope,
redirect_uri=self.redirect_uri,
)
@property
def redirect_uri(self):
return "http://127.0.0.1:" + str(webserver.PORT) + self.callback_url
def handle_auth(self, request):
parsed_path = urlparse(request.path)
query = parse_qs(parsed_path.query)
code = query.get("code")
if not code:
request.send_response(400)
request.send_header('Content-type', 'text/html')
request.end_headers()
request.wfile.write(b'Failed to authenticate. You can now close this window.')
webserver.stop()
if self.on_fail_callback:
self.on_fail_callback()
return
code = code[0]
tokens = self.get_tokens(code)
request.send_response(200)
request.send_header('Content-type', 'text/html')
request.end_headers()
request.wfile.write(b'Success! You can now close this window.')
webserver.stop()
if self.on_success_callback:
self.on_success_callback(tokens)
def get_tokens(self, code):
ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=certifi.where())
data = urlencode({
"grant_type": "authorization_code",
"code": code,
"client_id": self.client_id,
"client_secret": self.client_secret,
"redirect_uri": self.redirect_uri,
}).encode("utf-8")
headers = {"Content-Type": "application/x-www-form-urlencoded"}
req = Request(self.token_url, data=data, headers=headers, method="POST")
response = urlopen(req, context=ctx)
data = response.read().decode("utf-8")
data = json.loads(data)
return data
If I use the default OpenURL function Safari opens as the full blown browser rather than in-app, hence why I am trying to override it. When I run my app and click the button that SHOULD spawn the browser, nothing happens. I can see that my URL is getting pushed to the function in the log along with Warning: -[BETextInput attributedMarkedText] is unimplemented and Failed to request allowed query parameters from WebPrivacy. though my admittedly light research indicates this isn't an issue. I have a feeling I'm not attaching my webView to the right hierarchy but I'm not sure exactly what I'm doing wrong.
Post
Replies
Boosts
Views
Activity
Before anyone rants and raves about checking documentation - I have spent the last 4 hours trying to solve this issue on my own before asking for help. Coding in Swift is VERY new for me and I'm banging my head against the wall trying to teach myself. I am very humbly asking for help. If you refer me to documentation, that's fine but I need examples or it's going to go right over my head. Teaching myself is hard, please don't make it more difficult.
I have ONE swift file with everything in it.
import Foundation
import Cocoa
import Observation
class GlobalString: ObservableObject {
@Published var apiKey = ""
@Published var link = ""
}
struct ContentView: View {
@EnvironmentObject var globalString: GlobalString
var body: some View {
Form {
Section(header: Text("WallTaker for macOS").font(.title)) {
TextField(
"Link ID:",
text: $globalString.link
)
.disableAutocorrection(true)
TextField(
"API Key:",
text: $globalString.apiKey
)
.disableAutocorrection(true)
Button("Take My Wallpaper!") {
}
}
.padding()
}
.task {
await Wallpaper().fetchLink()
}
}
}
@main
struct WallTaker_for_macOSApp: App {
@AppStorage("showMenuBarExtra") private var showMenuBarExtra = true
@EnvironmentObject var globalString: GlobalString
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(GlobalString())
}
// MenuBarExtra("WallTaker for macOS", systemImage: "WarrenHead.png", isInserted: $showMenuBarExtra) {
// Button("Refresh") {
//// currentNumber = "1"
// }
// Button("Love It!") {
//// currentNumber = "2"
// }
// Button("Hate It!") {
//// currentNumber = "3"
// }
// Button("EXPLOSION!") {
// // currentNumber = "3"
// }
////
// }
}
}
class Wallpaper {
var url: URL? = nil
var lastPostUrl: URL? = nil
let mainMonitor: NSScreen
init() {
mainMonitor = NSScreen.main!
}
struct LinkResponse: Codable {
var post_url: String?
var set_by: String?
var updated_at: String
}
struct Link {
var postUrl: URL?
var setBy: String
var updatedAt: Date
}
func parseIsoDate(timestamp: String) -> Date? {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
return formatter.date(from: timestamp)
}
func fetchLink() async {
do {
url = URL(string: GlobalString().link)
let (data, _) = try await URLSession.shared.data(from: url!)
let decoder = JSONDecoder()
let linkResponse = try decoder.decode(LinkResponse.self, from: data)
let postUrl: URL? = linkResponse.post_url != nil ? URL(string: linkResponse.post_url!) : nil
let date = parseIsoDate(timestamp: linkResponse.updated_at)
let link = Link(
postUrl: postUrl,
setBy: linkResponse.set_by ?? "anon",
updatedAt: date ?? Date()
)
try update(link: link)
} catch {
}
}
func update(link: Link) throws {
guard let newPostUrl = link.postUrl else {
return
}
if (newPostUrl != lastPostUrl) {
lastPostUrl = newPostUrl
let tempFilePath = try getTempFilePath()
try downloadImageTo(sourceURL: newPostUrl, destinationURL: tempFilePath)
try applyWallpaper(url: tempFilePath)
} else {
}
}
private func applyWallpaper(url: URL) throws {
try NSWorkspace.shared.setDesktopImageURL(url, for: mainMonitor, options: [:])
}
private func getTempFilePath() throws -> URL {
let directory = NSTemporaryDirectory()
let fileName = NSUUID().uuidString
let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])!
return fullURL
}
private func downloadImageTo(sourceURL: URL, destinationURL: URL) throws {
let data = try Data(contentsOf: sourceURL)
try data.write(to: destinationURL)
}
}
The 'fetchLink' function is where things explode, specifically when setting the URL. I do not know what I'm doing wrong.