Post

Replies

Boosts

Views

Activity

Reply to How do I implement low-latency streaming between two local iOS devices?
Edit: @Eskimo Err come to think about it I should ask for one clarification regarding APs not forwarding STA-to-STA traffic before I burn myself: In your previous response you were ambiguous about the device brand, probably due to all the Peer-to-Peer Wi-Fi specifications out there. My use case is only between Apple products. Period. Does the phenomenon of some APs not forwarding STA-to-STA traffic apply to iPhone Hotspots talking with iPhones or iPads or Macs? E.g. Can an iPhone hosting a Hotspot Connection (being both AP and STA) and the iPhone being hosted (the pure STA) communicate with each other (e.g. cross streaming videos)? I did a double take and came back to ask because this is critical to me.
Feb ’22
Reply to Is it possible to compile images into an APNG using Swift?
I was hoping to find a Swift-based solution but there seems to be none. For whatever reason, both the AFathi's GIF generator and my APNG adaptation of it simply doesn't work. I suspect it has something to do with the UTType mess after kUTType was deprecated in iOS 15. I went ahead and adapted the Obj-C solution by Radif Sharafullin at https://github.com/radif/MSSticker-Images. I've included the updated code that has been confirmed to be working with iOS 17.2 below. The code is, as far as I can tell, basically the same as the Swift code, so I'm baffled as to why the Swift version doesn't work. If anyone figures out why, please let us know here! I won't mark this thread as having an answer since my stop-gap solution isn't Swift-based: #import "mcbAnimatedImagePersister.h" #import <ImageIO/ImageIO.h> #import <MobileCoreServices/MobileCoreServices.h> #import <UniformTypeIdentifiers/UniformTypeIdentifiers.h> @implementation mcbAnimatedImagePersister +(instancetype)shared{ static mcbAnimatedImagePersister * instance=nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance=mcbAnimatedImagePersister.new; }); return instance; } -(void)persistAnimatedImageSequenceGIF:(NSArray<UIImage *> *)images frameDelay:(CGFloat)frameDelay numberOfLoops:(NSInteger)numberOfLoops toURL:(NSURL *)toURL{ NSDictionary *fileProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id) kCGImagePropertyGIFLoopCount: @(numberOfLoops), } }; NSDictionary *frameProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id)kCGImagePropertyGIFDelayTime: @(frameDelay), } }; CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)toURL, (__bridge CFStringRef)UTTypeGIF.identifier, images.count, NULL); CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties); for (UIImage * image in images) { CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties); } if (!CGImageDestinationFinalize(destination)) { NSLog(@"failed to finalize image destination"); } CFRelease(destination); } -(void)persistAnimatedImageSequenceAPNG:(NSArray<UIImage *> *)images frameDelay:(CGFloat)frameDelay numberOfLoops:(NSInteger)numberOfLoops toURL:(NSURL *)toURL{ NSDictionary *fileProperties = @{ (__bridge id)kCGImagePropertyPNGDictionary: @{ (__bridge id)kCGImagePropertyAPNGLoopCount: @(numberOfLoops), } }; NSDictionary *frameProperties = @{ (__bridge id)kCGImagePropertyPNGDictionary: @{ (__bridge id)kCGImagePropertyAPNGDelayTime: @(frameDelay), } }; CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)toURL, (__bridge CFStringRef)UTTypePNG.identifier, images.count, NULL); CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties); for (UIImage * image in images) { CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties); } if (!CGImageDestinationFinalize(destination)) { NSLog(@"failed to finalize image destination"); } CFRelease(destination); } @end
Dec ’23
Reply to Scrolling sticker browser on a Messages App sheet causes sheet to move
I'm glad I added the SwiftUI tag, because I replicated the exact same issue using pure SwiftUI Unfortunately there doesn't seem to be anything published on how to fix the issue, even in pure SwiftUI--the ScrollView behaves as though the sheet is constantly expanded and transfers the drag gesture to the sheet when scrolled to the top (i.e. when first displayed), causing the user to move the sheet when intending to scroll in any direction. I can slap a band-aid on this by having ScrollViewReader scroll down a tiny bit upon appearing, but this is far from ideal since the issue re-emerges after the user has scrolled to the top. This is complicated by the fact that you can't disable detents in your Messages App. A workaround would be to abandon ScrollView (and MSStickerBrowserView) in favor of TabView with pages. struct Playground: View { @State private var detent = PresentationDetent.fraction(1/3) @State private var isSheetPresented = true var body: some View { Rectangle() .fill(Color(.systemGray5)) .sheet(isPresented: $isSheetPresented) { VStack { Text("ScrollView-in-Sheet Experiment") .padding() ScrollView { ScrollViewReader { scrollProxy in VStack(spacing: 0) { ForEach(0...10, id: \.self) { i in Rectangle() .fill(.white) .frame(height: 50) .id(i) .overlay { Text(i.description) } } } } } .frame(height: 200) .padding() } .background { Color(.systemGray6) } .presentationDetents([.large, .fraction(1/3)], selection: $detent) } } }
Jan ’24
Reply to Scrolling sticker browser on a Messages App sheet causes sheet to move
One solution is to stick everything in a SwiftUI TabView (with the .page style). You should be able to use either MSStickerBrowserView or MSStickerViews wrapped in a UIViewRepresentable struct Playground: View { @State private var isSheetPresented = true @State private var detent = PresentationDetent.fraction(1/3) var body: some View { Rectangle() .fill(Color(.systemGray5)) .sheet(isPresented: $isSheetPresented) { VStack { Text("ScrollView-in-Sheet Experiment") .padding() TabView { ScrollView { VStack(spacing: 0) { ForEach(0...100, id: \.self) { e in Rectangle() .fill(.white) .border(.gray, width: 0.3) .frame(width: 50) .overlay { Text(e.description) } } } } } .tabViewStyle(.page(indexDisplayMode: .never)) } .background { Color(.systemGray6) } .presentationDetents([.large, .fraction(1/3)], selection: $detent) } } }
Jan ’24
Reply to ScrollView behaves as though Sheet is always expanded when mutiple detents enabled
One solution is to stick everything in a TabView (with the .page style) struct Playground: View { @State private var detent = PresentationDetent.fraction(1/3) @State private var isSheetPresented = true var body: some View { Rectangle() .fill(Color(.systemGray5)) .sheet(isPresented: $isSheetPresented) { VStack { Text("ScrollView-in-Sheet Experiment") .padding() TabView { ScrollView { VStack(spacing: 0) { ForEach(0...10, id: \.self) { e in Rectangle() .fill(.white) .frame(height: 50) .overlay { Text("\(e)") } } } } } .tabViewStyle(.page(indexDisplayMode: .never)) } .background { Color(.systemGray6) } .presentationDetents([.large, .fraction(1/3)], selection: $detent) } } }
Jan ’24
Reply to Is the code in 'Building a custom peer-to-peer protocol' insecure?
I was away but I finally filed a report: FB13589481 Since it seems the code that was touted as secure is questionable at best, are there any alternatives? To get around some issues I'm attaching passwords to the Bonjour context and using a single, shared PSK (not stored as a string) to secure connections, but this is just a stopgap measure. Also, I reposted my comment to your reply to my other thread on wired data transfer between devices.. I'd be grateful if you would be kind enough to take a second look at my thread.
Feb ’24
Reply to Wired data transfer between an app on two iOS/iPadOS devices--Possible or pipe dream?
Reposting as reply since Quinn said that comments are lower-visibility to him Hi Quinn, I get what you're saying, but that would mean my users would have to get two USB dongles and an ethernet cable as opposed to just using the USB cable they already own. This is complicated by the fact that devices with a Lightning port need to attach the USB dongle to a Lightning-to-USB adaptor. At this point I'm not looking for ways to implement a USB-to-USB connection using just a single USB cable--I just want to know if the system(s) involved permit this sort of direct connection
Feb ’24
Reply to Wired data transfer between an app on two iOS/iPadOS devices--Possible or pipe dream?
tl;dr - Connect the devices and use the Network framework as usual. So I was figuring out another problem when I stumbled upon this thread: https://forums.developer.apple.com/forums/thread/658393 In it, Quinn demonstrates how to use the Network framework over pure USB (no ethernet dongle) to connect to a Mac. It turns out this also applies to two iOS devices (and maybe beyond). So far I've only tested it with two devices with Lightning ports connected by a Lightning-to-USB adaptor (no power attached) plus a USB-to-Lightning cable. Code to reproduce: class NetworkManager: ObservableObject { static var shared = NetworkManager() @Published private(set) var text = "" @Published private(set) var listenerQ: NWListener? @Published private(set) var browserQ: NWBrowser? var isListenerStarted: Bool { listenerQ != nil } var isBrowserStarted: Bool { browserQ != nil } func log(_ string: String) { print(string) text.append(string + "\n") } func startBrowser() -> NWBrowser { let browser = NWBrowser(for: .bonjour(type: "_customService._tcp", domain: nil), using: .tcp) browser.stateUpdateHandler = { self.log("Browser did change state, new: \($0)") } browser.browseResultsChangedHandler = { self.log("Browser did change results, new: \($0) \($1)") } browser.start(queue: .main) return browser } func stopBrowser(_ browser: NWBrowser) { log("Browser will stop") browser.stateUpdateHandler = nil browser.cancel() } func startStopBrowser() { if let browser = browserQ { browserQ = nil stopBrowser(browser) } else { browserQ = startBrowser() } } func startListener() -> NWListener { print("Listener will start") let listener = try! NWListener(using: .tcp) listener.service = .init(type: "_customService._tcp") listener.stateUpdateHandler = { print("Listener did change state, new: \(($0))") } listener.newConnectionHandler = { connection in let remotePeer = connection.currentPath?.remoteEndpoint self.log("Listener did receive connection, from: \(remotePeer)") connection.cancel() } listener.start(queue: .main) return listener } func stopListener(_ listener: NWListener) { log("Listener will stop") listener.stateUpdateHandler = nil listener.cancel() } func startStopListener() { if let listener = self.listenerQ { self.listenerQ = nil self.stopListener(listener) } else { self.listenerQ = self.startListener() } } } struct ContentView: View { @ObservedObject var netMan = networkManager var body: some View { VStack { HStack { Button("\(netMan.isListenerStarted ? "Stop" : "Start") listener") { netMan.startStopListener() } Button("\(netMan.isBrowserStarted ? "Stop" : "Start") browser") { netMan.startStopBrowser() } } .padding() Text("Log") .font(.title.bold()) ScrollView { Text(netMan.text) } } .padding() } }
Feb ’24