Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
fankey
Aug 31, 2001

lord funk posted:

Why are you drawing the fill after the stroke? Seems reversed to me.

Since the stroke is centered on the geometry, if you draw the stroke after the fill it will encroach on the glyphs.

Adbot
ADBOT LOVES YOU

fankey
Aug 31, 2001

Doc Block posted:

Take into account the width of the stroke when drawing your fill. See what happens when you make the stroke width 2 and then offset your fill by +1 +1 or +0.5 +0.5.

Also, it might be strangeness resulting from it being done with text rendering. Try converting that particular glyph into a CGPath and then drawing that.

Looks like the CGPath approach is the best - I can get a perfectly uniform stroke outline. The only problem is I can't get the correct glyph from my symbol font. I don't know if the issue is related to the fact that the font starts at glyph 0xF100 but when I pass the single character string ( which works perfectly fine with [NSString drawInRect:] ) I get the wrong glyph. The code below gives me 0xF121 when I pass in 'B' ( 0x0042 ). Something's is probably wrong in my CoreText code - I was mainly doing Physics 101 units conversion to get from a NSString to a CGPathRef.

code:
      CTFontRef f = CTFontCreateWithName((CFStringRef)@"fontcustom",imageView.frame.size.height, nil)
      CFDictionaryRef dict = CFDictionaryCreate( NULL, NULL, NULL, 0, NULL, NULL ):
      CFAttributedStringRef attStr = CFAttributedStringCreate ( NULL, (CFStringRef) @"B", dict );
      CTLineRef line = CTLineCreateWithAttributedString( attStr ) ;
      CFArrayRef runArray = CTLineGetGlyphRuns(line);
      CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runArray, 0);
      CFRange thisGlyphRange = CFRangeMake(0, 1);
      CGGlyph glyph;
      CGPoint position;
      CTRunGetGlyphs(run, thisGlyphRange, &glyph);
      CTRunGetPositions(run, thisGlyphRange, &position);
      CGPathRef path = CTFontCreatePathForGlyph(f, glyph, NULL);

fankey
Aug 31, 2001

I have an app which dynamically creates a bunch of UIViews, all of which override drawRect:. After upgrading to XCode 7.1 my memory usage has gone through the roof and am getting out of memory errors. I understand that overriding drawRect: will create a backing bitmap but did anything change with 7 or 7.1 to make doing this have a bigger memory footprint? I really need to have custom drawing for my views - is there a more memory efficient way to go about it compared to drawRect:? Maybe using CALayers or something?

EDIT: A little bit of data. In one example I'm creating 117 444x555 size images on an iPad Mini. By my simple math ( 444 x 555 x 4 bytes for ARGB ) each backing bitmap should be about 1MB so I should see about 117MB of memory usage. I'm seeing 480MB which would make sense if the backing bitmap was being created at retina resolution - maybe previously they were being created at the view resolution which was not the case before??

fankey fucked around with this message at 00:18 on Oct 27, 2015

fankey
Aug 31, 2001

I'm trying to do the following
code:
@interface Helper : NSObject 

@property (weak,readonly, nonatomic) UIView* parent;

@end

@implementation Helper
-(void)setParent:(UIView * _Nullable)parent
{
  if( _parent )
  {
    NSLog(@"removing watchers from parent");
    [self.parent removeObserver:self forKeyPath:@"iconUrl"];
    [self.parent removeObserver:self forKeyPath:@"iconData"];
  }
  _parent = parent;
  if( _parent )
  {
    NSLog(@"adding watchers from parent");
    // watch parent IconUrl change
    [self.parent addObserver:self forKeyPath:@"iconUrl" options:nil context:nil];
    [self.parent addObserver:self forKeyPath:@"iconData" options:nil context:nil];
  }
}
@end
But am running into issues since my parent only gets set to nil after it has been dealloced and the dealloc is failing since I still have watchers attached to the parent. Is there any way to use observers with weak references?

fankey
Aug 31, 2001

pokeyman posted:

Don't use weak?

In this case the parent has a reference to to the helper so I have a retain cycle I was trying to fix.

The odd thing is I have one class using the Helper class without any issues and another one is throwing a NSInternalInconsistencyException on dealloc. The usage is the same so I'm not sure what's going on there.

fankey fucked around with this message at 21:02 on May 23, 2016

fankey
Aug 31, 2001

rjmccall posted:

Weak reference don't magically call associated property setters when they get cleared; that's just not how the technology works.

Yeah I've figured that out. My code that was there to remove the observer was never getting called. The odd thing is that in one case the objects were able to be dealloced without any issue and in another case an exception is thrown. The usage is identical so the behavior is a mystery at this point.

One odd thing I'm noticing - if I step through dealloc on my Helper class the debugger has a valid value for _parent but printing _parent or any code that checks for parent against nil determines that it's nil.

I'm wondering if I should forgo KVO and just roll my own since I'm in control of all the classes that are in use.

fankey
Aug 31, 2001

I'm having issues with custom frameworks. I'm attempting to bring in SVGKit and following the instructions here - https://github.com/SVGKit/SVGKit. I can get it to work for a particular build type ( either iOS device or emulator ) but to do so I first have to rebuild the framework for the particular target, remove the existing framework and then re-drag in the new one. If I don't do this I get the following

code:
Undefined symbols for architecture i386:
  "_OBJC_CLASS_$_SVGKFastImageView", referenced from:
      objc-class-ref in Image.o
      objc-class-ref in ButtonHelper.o
  "_OBJC_CLASS_$_SVGKImage", referenced from:
      objc-class-ref in Image.o
      objc-class-ref in ButtonHelper.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

This seems like it can't be the right way to go about doing this so I'm obviously missing something but I haven't found the solution yet with random googling.

Edit: It looks like SVGKit includes a script to make a fat binary ( from here - http://stackoverflow.com/questions/3520977/build-fat-static-library-device-simulator-using-xcode-and-sdk-4 ) but for whatever reason it still fails when I attempt to build my project. hmmm...

fankey fucked around with this message at 17:24 on Jun 17, 2016

fankey
Aug 31, 2001

Doc Block posted:

Yeah, you need a fat binary if you want just one .framework

After you build the fat binary, open Terminal.app, cd to the directory that the binary inside the .framework is in, then type "file [binary file name]" and it should show you all architectures included in that binary.

The error message says it's missing code for i386, so that means the 32-bit simulator, so make sure an i386 section is in your fat binary. Or run the 64-bit simulator, if the framework has x86-64 but not i386.

Another way to do things would be to just drag the library's Xcode project into your Xcode project, then in the build settings set its static library target as a dependency and the static library output as a library to link against. Xcode will then always build the static lib for whatever architecture you're trying to test on, and build it for all of them when you do your distribution build.

When I'm back at my computer I'll look at that lib and see if I can figure out why it isn't getting built as a fat binary.

Looks like the framework I built only includes ARM
code:
SVGKit: Mach-O universal binary with 2 architectures
SVGKit (for architecture armv7):	Mach-O dynamically linked shared library arm
SVGKit (for architecture arm64):	Mach-O 64-bit dynamically linked shared library
I can't figure out how to force Xcode to build the Framework with both ARM and x86 together. I can get it to build a universal static library but not the framework.

fankey fucked around with this message at 20:18 on Jun 17, 2016

fankey
Aug 31, 2001

eschaton posted:

If you're using pre-built frameworks for different platforms, I'd recommend having them in sub folders of whatever the SDK folder is that are named for the platform identifier. E.g. FooSDK/iphoneos/Foo.framework and FooSDK/iphonesimulator/Foo.framework and so on. Since Xcode will search for the framework by name at link time, you can just then have /path/to/FooSDK/$(PLATFORM_NAME) be the value that's in your Framework Search Paths build setting.

If you're building the framework yourself, just having the project that builds it be in the same workspace as your app project should be sufficient to ensure that when you're building for iOS everything goes into the Debug-iphoneos directory and when you're building for iOS Simulator goes into the Debug-iphonesimulator directory and so on, based on the run destination you've selected in Xcode's toolbar. If that's not happening my first suspicion would be that something is trying to override BUILT_PRODUCTS_DIR or another setting like it, and not accurately replicating how Xcode varies it per platform.

Thanks for the info. I got the build working using the $(PLATFORM_NAME) scheme. Since the library is dynamic I'm having issues with running. It seems like the 'Embedded Binaries' is ignoring the $(PLATFORM_NAME) since it's complaining that no matching architecture is found. When I add the Framework to the Embedded Binaries list it shows the path to a specific Framework ( in this case the one that works ) in italics and I can't see a way to use PLATFORM_NAME as an indirection.

I'd try adding everything to the same workspace but my project has somehow been broken in a way that makes it not possible to add it to a workspace - it just shows up as a single item without all the supporting stuff. This is probably due to me attempting to de-workspace when I was also de-cocoapodding since that was causing more problems than it was solving.

One other question - I used Carthage to build a different library ( CocoaAsyncSocket ) and it's working without any issues and shows all architectures in one Framework file
code:
CocoaAsyncSocket: Mach-O universal binary with 4 architectures
CocoaAsyncSocket (for architecture i386):	Mach-O dynamically linked shared library i386
CocoaAsyncSocket (for architecture x86_64):	Mach-O 64-bit dynamically linked shared library x86_64
CocoaAsyncSocket (for architecture armv7):	Mach-O dynamically linked shared library arm
CocoaAsyncSocket (for architecture arm64):	Mach-O 64-bit dynamically linked shared library
Is this a bad idea?

fankey fucked around with this message at 23:02 on Jun 17, 2016

fankey
Aug 31, 2001

Doc Block posted:

Check the PM I sent you.
It's working now. Thanks!

fankey
Aug 31, 2001

After adding a 3rd party framework to my App every time I archive the result shows up in the Other Items list in the archive manager, not in iOS Apps. Searching around I see lots of different solutions to this problem - of course none of them are fixing the issue for me. Is there any way to get any debug or log information to determine why this is happening or am I doomed to search Stack Overflow and randomly try things until I get it working?

fankey
Aug 31, 2001

Doc Block posted:

Is the framework actually built for iOS? Is it just a closed source binary, or do you have the source code?

Yes. It's the SVGKit framework which I had added as a subproject to mine. Builds and runs fine both in the emulator and on a device. For now I've just pulled their source directly into my project which is building and archiving without issues.

fankey
Aug 31, 2001

Doc Block posted:

Assuming you're adding it the way I mentioned earlier, I don't think SVGKit is the problem. I just built and archived a test app that contains SVGKit and it got added to the iOS Apps section of the Organizer window.

SVGKit is the only 3rd party framework I'm including and moving the source directly into my project fixed the issue so there was some interaction with the framework that was causing issues. It could be a setting in my own project causing the issue but without any information from Xcode as to what the actual problem is I'm not sure what's going on.

fankey
Aug 31, 2001

Ender.uNF posted:

Were you archiving the correct scheme? Did the archive's bundle identifier still match your app or did it change?

As far as I know I was archiving the correct scheme - I only have one defined in my project. The version and Identifier are blank in the Archive manager and the Type is 'Generic XCode Archive'.

fankey
Aug 31, 2001

Doc Block posted:

Was your project linking to the right SVGKit platform (iOS)? If the archiver finds something it doesn't like inside the final built product then (if it builds the archive at all) it will call it a "Generic Xcode Archive".

Yes, libSVGKit-iOS.2.0.0.a is listed in Linked Frameworks and Libraries. I removed SVGKits references to CocoaLogger and still had the issue. As this point I think I'm going to leave the source directly in my project and move on.

fankey
Aug 31, 2001

Doc Block posted:

You could always open the archive and see what's in there. Like apps, the bundle is just a folder. Right click->Show Package Contents

Would having the SVGKit headers included in the archive cause the issue? They are in /Products/usr/local/include. If so, how do I stop them from being included? I tried setting Skip Install to be Yes on SVGKit but that didn't remove them from the archive.

fankey
Aug 31, 2001

Any suggestions on how to track down why after updating to XCode 8.2.1, the IDE completely locks up with 99% CPU usage 10 seconds every time loading my project? This is a standard ObjC project without any Swift, CocoaPods, etc. I don't see anything interesting the the Console logs.

Edit: It finally came back after 45 minutes. I think one of the issues is that I'm checking out a sparse git checkout and XCode thinks there are 10,000s of missing files which it feels necessary to report to me, even though none of these file are referenced in the Workspace. Is it possible to work with sparse checkouts in XCode or am I going to be frustrated?

fankey fucked around with this message at 23:35 on Mar 17, 2017

fankey
Aug 31, 2001

I just updated to XCode 9.2 and I'm running into a lot of issues with the simulator that I'm pretty sure didn't occur in previous versions. I haven't narrowed things down to simple test cases yet but one thing I've noticed is that if I change the UI via a touch interaction ( automatically scrolling a scroll viewer, setting UIView.hidden, etc ) things seem to lag one update when running in the simulator but function just fine on real devices. I'm totally open to the problem being in my code but I'm curious if anyone else has seen weird performance using the new simulators. I'm also seeing a bunch of app crashes in the simulator that I can't reproduce on real devices.

fankey
Aug 31, 2001

Do you mean deleting and readding via the 'Add Additional Simulators..' item at the end of the simulators list? I tried removing and adding one and it didn't fix the issue. Do I need to remove them all?

fankey
Aug 31, 2001

Simulated posted:

Do you have an Intel HD 3000 GPU or something similar?

As a matter of fact yes - MacBook Air Mid 2011.

fankey
Aug 31, 2001

I'm attempting to use NSScanner to parse CSS style colors - rgb(), rgba(), red, blue, #RRGGBB, etc. I sorta have something working to deal with rgb/rgba
code:
#import <Foundation/Foundation.h>

void scan(NSString* str)
{
  NSLog(@"parsing '%@'", str);
  NSString *funcName = nil;
  NSCharacterSet* parenSet = [NSCharacterSet characterSetWithCharactersInString:@"("];
  NSScanner* theScanner = [NSScanner scannerWithString:str];

  if([theScanner scanUpToCharactersFromSet:parenSet intoString:&funcName] &&
     [theScanner scanString:@"(" intoString:NULL])
  {
    if( [funcName isEqualToString:@"rgb"] || [funcName isEqualToString:@"rgba"])
    {
      int r,g,b;
      if([theScanner scanInt:&r] &&
         [theScanner scanString:@"," intoString:NULL] &&
         [theScanner scanInt:&g] &&
         [theScanner scanString:@"," intoString:NULL] &&
         [theScanner scanInt:&b])
      {
        if( [funcName isEqualToString:@"rgba"])
        {
          float a;
          if([theScanner scanString:@"," intoString:NULL] &&
             [theScanner scanFloat:&a])
          {
            NSLog(@"  GOT COLOR R %i G %i B %i A %f", r, g, b, a);
            return; // a UIColor at some point
          }
        }
        else
        {
          NSLog(@"  GOT COLOR R %i G %i B %i", r, g, b);
          return; // a UIColor at some point
        }
      }
    }
  }
  NSLog(@"  PARSE FAILED!");
}

int main(int argc, const char * argv[]) {
  @autoreleasepool
  {
    scan(@"rgb(12, 33, 12)");
    scan(@"rgba(12, 33, 12)");
    scan(@"rgba(12, 33, 12, .3)");
    scan(@"rgb( foo, 33, 12)");
    scan(@"rbg( foo, 33, 12)");
    scan(@"butt( 12, 33, 12)");
    scan(@"rgb 12, 33, 12)");
  }
  return 0;
}
prints
code:
2019-04-19 10:59:07.581163-0600 scannertest[12217:502752] parsing 'rgb(12, 33, 12)'
2019-04-19 10:59:07.582016-0600 scannertest[12217:502752]   GOT COLOR R 12 G 33 B 12
2019-04-19 10:59:07.582302-0600 scannertest[12217:502752] parsing 'rgba(12, 33, 12)'
2019-04-19 10:59:07.582355-0600 scannertest[12217:502752]   PARSE FAILED!
2019-04-19 10:59:07.582378-0600 scannertest[12217:502752] parsing 'rgba(12, 33, 12, .3)'
2019-04-19 10:59:07.582414-0600 scannertest[12217:502752]   GOT COLOR R 12 G 33 B 12 A 0.300000
2019-04-19 10:59:07.582443-0600 scannertest[12217:502752] parsing 'rgb( foo, 33, 12)'
2019-04-19 10:59:07.582475-0600 scannertest[12217:502752]   PARSE FAILED!
2019-04-19 10:59:07.582495-0600 scannertest[12217:502752] parsing 'rbg( foo, 33, 12)'
2019-04-19 10:59:07.582520-0600 scannertest[12217:502752]   PARSE FAILED!
2019-04-19 10:59:07.582540-0600 scannertest[12217:502752] parsing 'butt( 12, 33, 12)'
2019-04-19 10:59:07.582564-0600 scannertest[12217:502752]   PARSE FAILED!
2019-04-19 10:59:07.582584-0600 scannertest[12217:502752] parsing 'rgb 12, 33, 12)'
2019-04-19 10:59:07.582607-0600 scannertest[12217:502752]   PARSE FAILED!
This doesn't even cover all the function cases ( it breaks if there is a space between the function name and the ( which I think is valid in CSS. I'm not sure if there is better, more robust way to this? It's also not clear to me if it's possible to use it to deal with color functions, color strings as well as # hex definitions.

fankey
Aug 31, 2001

Doctor w-rw-rw- posted:

indexOf rgba/indexOf rob followed by replace white lspace with nothing and split from index after ( on comma, assert the last char is ).

If that fails check for length =3,4,6,8 and that the character lowercases are in range. If 3 or 4 then double up the characters and get the number from that otherwise just get the number for it

Normalize to 0..1 and create your color from those components

Thanks. That's pretty much where I ended up - using NSScanner for the function parsing since it's pretty easy to get the arguments but then fall back and split the string apart manually if it's hex.

fankey
Aug 31, 2001

Thanks for the parser comments. I ended up finding a parser on github that met my needs so crisis averted.

Now another question - is there a straightforward way to draw rounded rectangles in CoreGraphics that have different horizontal and vertical radii? Basically implementing this. WPF has a handy ArcSegment which allows you to specify both a x and y radius for the arc. All the arc functions I see in CoreGraphics only take a single radius and I'm hoping I'm not doomed to try and piece-wise approximate using bezier curves - although if someone knows a handy reference there please let me know!

fankey
Aug 31, 2001

Thanks. Somehow I missed that one in my searching. Worked great!

fankey
Aug 31, 2001

I have an app that works fine in the emulator, fine on my device connected to XCode but fails when running the exact same codebase via TestFlight. Is there a way to reproduce what is being run via TestFlight without going throught the whole cycle of uploading the app, etc? The app draws a bunch of stuff via CoreGraphics and in this case fail means doesn't draw all the elements.

fankey
Aug 31, 2001

Glimm posted:

Switching the build configuration in your scheme to your production config might help.

That worked - I can now reproduce bad behavior in the simulator. Now the problem is I have no idea what's going on. It's probably something obvious but here's my code.
code:
        // defined elsewhere
        // float pad
        // int ix
        // CGRect rc
        // float size

        NSLog(@"draw pad %f or.y %f ix %i sz %f", pad, rc.origin.y, ix, size);
        float y = rc.origin.y + ix*(size+pad);
        NSLog(@"draw y %f", y);
prints
code:
draw pad 0.517500 or.y 0.000000 ix 6 sz 9.297188
draw y nan
How does adding and multiplying valid numbers result in a nan?

fankey
Aug 31, 2001

Digging into this more, this code
code:
        NSLog(@"pad %f : 0x%08x", pad, *((uint32_t*)(&pad)));
        NSLog(@"rc.origin.y %f : 0x%08x", rc.origin.y, *((uint32_t*)(&rc.origin.y)));
        NSLog(@"size %f : 0x%08x", size, *((uint32_t*)(&size)));
        float y = rc.origin.y + ix*(size+pad);
        NSLog(@"y %f : 0x%08x", y, *((uint32_t*)(&y)));
produces
code:
pad 0.517500 : 0x3f047ae1
rc.origin.y 0.000000 : 0xfd802000
size 9.297188 : 0x4114c148
y nan : 0x7fc00000
rc.origin.y isn't actually zero but a really small negative number ( -2.1288417E37 ). Is this and underflow case that is triggering a NAN? If so, is there a way to disable underflow checks? I tried casting everything to doubles but it appeared to have the same behavior.

fankey
Aug 31, 2001

Doc Block posted:

It’d probably be a good idea to clamp some of those values to sane ranges before doing any math on them, like maybe not allowing sizes smaller than 1 or something.

edit: also, you're mixing doubles, floats, and integers. There's bound to be an unintended precision problem because of this.

rc.origin.y is a CGFloat, which is typedef'ed to double on 64-bit platforms, not float. So your cast would need to be to a uint64_t, and your format string needs %016llx

edit 2: using your supplied values, I can't get your results, even compiling with -O3 and -ffast-math

edit 3: even plugging in your hex NaN value for Y, printf() shows 2143289344.0

set a breakpoint somewhere in there and see where the NaN is coming from; they're like viruses, and will "infect" all downstream math.
Figured out the problem - I'm an idiot and rc was uninitialized so in release mode it was a NaN. Printing out the CGFloat as a uint64 made it obvious. On the plus side I learned more about floating point binary formats...

fankey
Aug 31, 2001

I have a subproject which builds dylib that my main project is linking to and I can't figure out how to get the code signing to work so I can build and archive. I am able to emulate without any issue.

The error I'm getting is this
code:
Signing for "antlr4" requires a development team. Select a development team in the Signing & Capabilities editor.
The project looks like this






Originally the antlr4 target had a 'Choose Identity' button ( or something similar ). I clicked on that and chose the antlr4_ios.plist and that filled out that section. I've made sure the Team matches my main app and set the every bundle identifier I can find to match with the understanding if I did that I wouldn't need to sign the dylib but I still can't get past that build error.

Edit: Noticed that in the main project that libantlr4-runtime.dylib was set to 'Embed & Sign'. Changing that to 'Embed Without Signing' has no affect and it still looks like it's attempting to sign the dylib for some reason.

fankey fucked around with this message at 19:21 on Dec 3, 2020

fankey
Aug 31, 2001

Doc Block posted:

For iOS you always need to sign.

The error message is explicitly saying it can’t be signed because you don’t have a team specified, and in your screenshots you don’t have a team or bundle specified.

Also, shipping it with your app as a framework instead of a dylib is probably gonna be the path of least resistance in the long run.

Are you talking about the black boxes in the screenshots? That was just me editing the screenshot to subvert the NSA - they are actually set to the same Team and Bundle as my main app.

Is there a straight forward way to convert the dylib project to a Framework?

fankey
Aug 31, 2001

Doc Block posted:

Ohhh... that's what I get for phoneposting :haw:

edit: I would probably just make a new Framework target, and add files to it however you need them. Then you can have your app use that instead of the dylib and no custom include directories or any of that.

Thanks for pointing me in that direction - I was able to create a Framework as a child of my project and with a little finagling got everything uploaded to appstoreconnect and get it from TestFlight. I do have a couple of questions since I don't really understand Frameworks.

The ANTLR4 source is huge and when it's part of the main project the whole thing builds on every Archive which on my Air takes over 5 minutes. It seems like the best thing would be to remove it from my main workspace, build it separately and then reference the Framework so I only need to rebuild if the Framework source changes. I can open up the Framework project in another copy of Xcode and drag it in but then I either get a 'Framework not found error' when Archiving for the App Store or 'Building for iOS Simulator, but the linked framework was build for iOS'. So either the way I'm building the framework is wrong or the way I'm including it is.

The other thing that's odd - when I had the framework as part of the main project I had to select 'Embed & Sign' on that framework to get it up into appstoreconnect. What's odd is that I have other frameworks build locally ( using Carthage, another piece of magic I don't understand... ) and those are set to 'Not Embed' and work fine. What's different between the way Carthage is bringing in frameworks vs what I was doing?

Tried following this https://medium.com/allatoneplace/writing-custom-universal-framework-in-xcode-9-and-ios-11-7a63a2ce024a and had no luck. The script didn't generate a shortcut in the project folder and the resultant build archive didn't contain any of the framework.

fankey fucked around with this message at 22:27 on Dec 3, 2020

fankey
Aug 31, 2001

In objc, is there an easy way to chain multiple NSURLSession.dataTaskWithURL calls? I need to do something like this procedural pseudo code
code:
data = HttpGetData(someUrl); // call that gets some data structure via HTTP

List<Foo> foos;
foreach(foo in data.foos)
  foos.add( HttpGetFoo(foo));

List<Bar> bars;
foreach(bar in data.bars)
  bars.add( HttpGetBar(bar, foos));

CallSomeFunction( foos, bars )
In C# async land this is trivial. Trying to chain together completionHandlers to then trigger the next step in the process is ugly, especially if different parts of the process need different arguments and are handled by different classes. Is there anything built in or available via a lightweight 3rd party lib that can help make this easier to deal with?

fankey
Aug 31, 2001

Thanks for the input. I'd prefer that they run in order so I don't have to worry about 429 errors or something similar. I don't really need to load things as fast and as parallel as possible. There might be a different approach - let me explain better what I'm trying to accomplish.

I'm loading a bunch of images from an HTTP server. The current version of the app creates a separate dispatch_queue_t, uses CGImageSourceCreateWithURL to create the images and once finished makes a callback back to the dispatch_get_main_queue to notify the UI that the images have been loaded. This worked perfectly fine until we wanted to use HTTPS instead of HTTP to load the images. Because this server is an internal network appliance the certs will either be self signed or likely not installed on the iOS device. Since the certs can't be verified CGImageSourceCreateWithURL fails due to TLS issues. I do know that I can automatically trust the cert when using the URLSession:didReceiveChallenge callback. Is there a way to accomplish the same without using the async URLSession methods?

fankey
Aug 31, 2001

That looks pretty fancy. Unfortunately I haven't bothered to learn Swift yet so to me it's mumbo jumbo :shrug:

Here's something that I came up with. Seems to work and is lacking in a bunch of error handling. It's a class you can hand a bunch of URLs, tell to go and get told when it's done. It looks like it may be kind of similar to what you were suggesting.
code:
@interface DataDownloader : NSObject<NSURLSessionDelegate>
{
  NSMutableDictionary<NSString*,NSURL*>* queue;
  NSMutableDictionary<NSString*,NSData*>* data;
  NSURLSession* session;
}
typedef void(^download_complete)(NSDictionary<NSString*,NSData*>*);
@property (nonatomic) download_complete completion;

@end
@implementation DataDownloader
-(id)init
{
  if(self = [super init])
  {
    queue = [[NSMutableDictionary<NSString*,NSURL*> alloc] init];
    data = [[NSMutableDictionary<NSString*,NSData*> alloc] init];
    NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
    session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
  }
  return self;
}

-(void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
{
  if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
    
    NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
  }
}

-(void)addUrl:(NSURL*)url forKey:(NSString*)key
{
  [queue setObject:url forKey:key];
}
-(void)go
{
  if(queue.allKeys.count > 0)
  {
    NSString* key = queue.allKeys[0];
    NSURL* url = queue[key];
    [queue removeObjectForKey:key];
    NSURLSessionDataTask *task = [session
      dataTaskWithURL:url
    completionHandler:^(NSData *dt, NSURLResponse *response, NSError *err)
    {
      if(err)
      {
        NSLog(@"ERROR : %@", err);
      }
      else
      {
        [self->data setObject:dt forKey:key];
      }
      [self go];
    }];
    [task resume];
  }
  else
  {
    dispatch_async(dispatch_get_main_queue(), ^{
      self.completion(self->data);
    });
  }
}
@end

fankey
Aug 31, 2001

Anyone have any experience with the the apparently new limitations on using mulitcast in apps? Our app both sends and receives multicast UDP in order to discover devices on the local network. It appears as though 14.6 broke this. I've found this page https://developer.apple.com/news/?id=0oi77447. That page has a link titled Request the com.apple.developer.networking.multicast entitlement which sends you to an odd page where you have to manually fill things out like App ID, App Name, Description, etc. I would have assumed Apple could have figured out most of this information since the app is registered but whatever. We've filled out the form and added the 'Privacy - Local Network Usage Description' string to the plist but then what happens? We haven't received any confirmation or additional information after submitting the form. At some point does someone wave their magic wand and our app starts working? Will we need to resubmit after they've done whatever they need to do?

fankey
Aug 31, 2001

Simulated posted:

If at all possible I highly recommend using Bonjour. It's open source (see mDNSResponder) and built-in basically everywhere at this point.
We switched from Bonjour to our own discovery method over 10 years ago. It didn't really seem sufficient for modern 3 tier networks. The main issues were (1) it's not routable without an mDNS gateway and (2) the automatic query interval increase means joining 2 'steady-state' networks can mean waiting up to an hour for devices to discover each other. Couple that with the requirement to support Windows with Apples schizophrenic support of Bonjour on Windows I think we made the right choice. It's possible we either misunderstood these limitations or they have since been addressed but it's not clear to me how anyone could use Bonjour beyond a simple flat network.

We did get a response from Apple regarding our usage of multicast so hopefully we'll get a resolution soon.

fankey
Aug 31, 2001

Simulated posted:

Great, let me know if you have trouble getting a response and I'll see if I can find the right person to poke.

I assume your users are in corp networks where different devices are on separate VLANs and that's why normal Bonjour wasn't working for you? A lot of commercial WiFi APs, routers, etc have built-in support for Bonjour proxying and management now. You can configure the rules to decide which devices on which VLANs or subsets get shared with others on the service type level, so eg you can allow APs in a specific building to proxy AirPrint advertisements from that building's printer VLAN to the whole building (via all APs and desktop VLANs) but restrict music sharing or AirPlay to be only devices connected to the same AP.

Regarding #2: normally a new service announces itself when connecting so you shouldn't see a delay when browsing for new services and similarly a host looking for new services can query that at any time so it should fine them immediately despite the exponential backoff caching. Moving networks should automatically reset the local cache so if a device moves from one VLAN to another (eg by switching APs) it should clear its own Bonjour cache and there shouldn't be a delay in discovering devices on the new network.

If you have specific scenarios that don't work or other concerns feel free to DM me a feedback number and I can forward this to the networking team.

Thanks for the offer. Hopefully they will respond in the next week or so.

Yeah, a lot of our customers are on Cisco style 3 tier networks with lots of routing. The particular job that blew up for us was a cruise ship where every room was on it's own subnet. Not sure exactly what they were trying to solve. At the time I don't think the Bonjour routing in the switches was a thing.

When a device detects a change to it it's network connectivity due to link change or AP switch it will definitely reset the interval and there aren't any issues. The problem we were seeing was due to network changes that the devices cannot detect. For example - device 1 hooked up to switch 1, device 2 hooked up to switch 2. Let that sit for an hour or so. Now connect switch 1 to switch 2. Each device will have slowed down their interval ( I think the slowest they end up at is 1 hour ) but there is nothing to inform the devices that they need to clear their cache and reset the interval.

fankey
Aug 31, 2001

My app supports user supplied fonts which I draw with CATextLayer. Most fonts work just fine but some are 'bullshit fonts' like this one - https://www.dafont.com/cubic.font. In that font the author decided to have font content go below the descender ( noted below with the yellow line ). I'm not sure that's legal for a font. I do have rendering implementations based in both Qt and WPF that render the font fine but I'm struggling with trying to get iOS to not clip the bottoms off.



I'm able to read the UIFont.ascender and UIFont.descender to adjust my bounds and offsets so I don't clip things within those but I can't figure how to to determine, for a particular font, how much it extends beyond the descender. Is this possible with UIFont?

fankey
Aug 31, 2001

I made the always poor choice of upgrading Xcode to 13.2.1 and now I'm running into code signing issues. I have another project that I've dragged in as a Framework and had the signing set up like this

free img host
When I attempt to archive the app I get the following error
code:
Warning: unable to build chain to self-signed root for signer "Apple Development: XXXXXX"
/Users/XXXXXXX/Library/Developer/Xcode/DerivedData/XXXXXX-egshcztqxjcvajccurikbgmmndvq/Build/Intermediates.noindex/ArchiveIntermediates/QSys Controller/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/ANTLR4.framework: errSecInternalComponent
Command CodeSign failed with a nonzero exit code
I've both restarted Xcode and removed my developer account and re-added it in the Xcode prefs but neither fixed the issue. Any ideas what to try beyond installing the previous version of Xcode to see if that fixes the issue?

Adbot
ADBOT LOVES YOU

fankey
Aug 31, 2001

Any idea what Project setting would break using c++ 14 features in .mm files? I have a project that has a .mm file which then includes a .h file with the following
code:
#ifndef Test_h
#define Test_h
template<typename T>
constexpr auto enum_flag2(const T n) { return 1 << n; }

enum control_direction2_e {
  foo = 0
  bar = enum_flag2(0),
};

#endif /* Test_h */
And when compiles errors out with
code:
Test.h:12:11: error: 'auto' return without trailing return type; deduced return types are a C++14 extension
constexpr auto enum_flag2(const T n) { return 1 << n; }
          ^
XXX/Test.h:15:10: error: missing ',' between enumerators
  foo = 0
         ^
         , 
XXX/Test.h:16:9: error: no matching function for call to 'enum_flag2'
  bar = enum_flag2(0),
        ^~~~~~~~~~
3 errors generated.
I have set the C++ Language Dialect to GNU++17 and if I include the file in a .cpp file it compiles without any issue. If I create a brand new Project things work correctly as well so I'm guessing that the issue is related to a specific Project setting. This particular Project has been drug through various versions of Xcode ( likely since the iPad was first released or so ) and it has a bunch of settings that don't exist in the brand new project, though none of the settings seem particularly suspicious.

Working Project :


Broken Project :

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply