XCTest – Fixing audio/video UI tests on your CI/CD

You can use UI tests to verify the behavior of audio or video players in your app, but they may fail on a CI/CD environment. You can find the fix in this post.

Before going any further, let’s quickly get to the bottom of the issue. CI providers use virtual machines without sound because they are primarily used for running automated tests and building software projects, rather than for tasks that require audio output. When test runner opens the media player on your app, the app will do nothing. Interestingly enough, it won’t even crash -simply will remain silent on the player screen (pun intended).

One fix I found on the internet is attaching a null audio driver before running UI tests. Here’s how I do it in my CircleCI config:

run-ui-tests:
  steps:
    - run:
        name: Attach null audio driver
        command: |
          git clone --depth 1 --branch v0.1.1 https://github.com/kyleneideck/BackgroundMusic.git
          cd BackgroundMusic
          sudo xcodebuild -project BGMApp/BGMAppTests/NullAudio/AudioDriverExamples.xcodeproj -target NullAudio DSTROOT="/" install
          sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod || sudo killall coreaudiod
          cd .. && sudo rm -rf BackgroundMusic

What we are doing here is quite straightforward:

  • We clone a repository named BackgroundMusic. We use a specific version as they seem to have removed the code we need in the following versions.
  • We build the NullAudio project and attach it to the system
  • We delete the code that’s no longer needed.

With this fix, your UI tests should run as they do locally. One important thing to note here is that we are using an old version of the repository from 5 years ago and you might encounter warnings on your CI when building the audio driver project (might even crash on the future versions of MacOS). I’ll be sure to update here if I’ll have to come up with a fix for that.

I created a small project which demonstrates this scenario and has the fix. Here’s the GitHub link.

Credits

After the first-ever Mobile DevOps Summit

Last week, I attended Bitrise’s Mobile DevOps Summit 2022. It was a one-day virtual event packed with dozens of talks from people all around the world. As someone who spent more than ten years in mobile development and has a full-stack background, let me start by appreciating the notion of “Mobile DevOps” and Bitrise for taking it seriously. If I’m being honest, there were times in my career when the usual DevOps felt like force-fitting for the mobile and it’s great to see the shift in how the industry is approaching mobile-related operations nowadays.

If you haven’t heard of this event or didn’t have the time to participate, I highly recommend checking out the agenda and watching the sessions you find interesting, they’re all available on demand.

Before sharing my picks below, I’ll say a few words on the event. When I saw the agenda for the first time, with 25-min talks back to back within almost 8 hours and in parallel, I wasn’t expecting to survive after the first half. Things actually played out quite nicely for me, and I think the credit goes both to the speakers for preparing their talks short and concise as well as to the moderators for being mindful about the time. Although sometimes the talks were just cut-off after the 25-min remark, most of the times there were enough time to wrap things up.

Here’s my top 3 from the talks

#1 – Best Practices in Automated Testing by Ron Diamond

Ever since I started to feel like getting a grasp of BDD and its potential, I never stopped thinking about the ways of implementing it. I believe it’s a never-ending process – there’s no one-size-fits-all way of doing it, and that’s the exciting part. But what are some practices to apply on the fundamentals to make your life much easier as you invest more time on the upper layers of your test suite? Look nowhere else because this talk will give you lots of food for thought.

Bonus material: Blog post by the author

#2 – The Journey to Weekly Releases at eBay by Wyatt Webb

There are a lot of “easier said than done” things in software engineering. Release process is definitely one of them.

Have you ever worked on a project where you built the right thing, you built it well and it works and everyone’s happy, you pop the cork in the champagne and then you find out that it’s not going to be deployed for three months?

If you want to know what situation your company is in, try to deploy “hello world” and see how long it takes.

Talk by J.B. Rainsberger – The Economics of Software Design

Okay, this talk might not be about the example I quoted above, but I don’t think it’s too far away as it’s for shortening the release cycle. How easy is it to reduce your release cycle by one day? Maybe fairly. Two days? What about three? If you are releasing bi-weekly, then what about cutting it in half!?

So, what I liked about Wyatt Webb’s speech is that it’s not a concept, it’s a demonstration and an aftermath. I believe there’s a lot to learn there, but thankfully for us, from the easy way.

#3 – Level up your CI for iOS and macOS by Philipp Hofmann

Can you get away with not speaking about automation and optimization in a DevOps event hosted by a CI/CD platform? (Why would you, anyway) This one is a bit more technical and iOS/macOS oriented – Philipp impressed me with great tips and tricks for getting the best out of Apple frameworks for your CI.

Bonus material: The decks

And finally…

Here are some other talks that I enjoyed – hope there’s something for you!

The app you built has to be on the App Store (as long as it compiles)

Back in the day when I was getting into cycling, although I didn’t know what to expect, I knew one thing for sure: I had to pick the bike with the right frame. It didn’t take too long until I found myself inside an ocean of parameters because I came to notice that bike frames had many attributes alongside the size. One of them was the angles of where the parts meet each other and depending on these, the bike would give you a different ride experience like a comfortable one, or an aerodynamic one, and such.

I was serious, and I had to pick not just any frame, but the best for my needs. Well, there was a problem: Not every buyer or seller cared about every detail, and I wasn’t able to find all the details of the frames I was looking at, more specifically, the angles within the frame body.

So I came up with an idea: How about I make a small app that’s just for solving the problem at hand: Measuring the angles on a given photo. Did I do that right away? Yes. Did I use it for measuring angles on bike frames? Yes. Did I get the “best” frame for myself? No, but that’s another topic. What followed all of these is that I decided to put that app on the app store. It was awfully primitive, probably had the worst UX ever, and the code behind was… Catastrophic, to say the least. But I wasn’t expecting anything, to be honest. It was a small effort for me to publish and I would be more than happy if it just helped someone, somehow.

The app was being downloaded and used over the years, but I wasn’t getting any feedback, although that was fine. The thing about the internet is that it’s like a sea into which you throw your bottle with a note. You don’t know what will happen to the bottle, or if someone will ever read your note, but most importantly, whether you’ll ever get a response. Because we all fetch other people’s bottles, but we don’t always respond.

Well, after 3 years, I received a bottle. It was from a senior who presented himself as “a blue-collar guy or laborer” who had an injury at work and would never be the way he was. He had to measure the angles of the stairs he fell to see whether they match the industrial staircase requirements. He wanted my help because he thought that he was not educated in this discipline although it was a simple calculation (I told you that the app UX was horrible). He wanted to pay me for my service but I refused since I was more than happy to help, then he asked for a charity that he can donate to instead. I was just a guy who uploaded the app he built for himself, and not only did I help a handicapped senior from the other half of the world, a charity was getting a donation because of me! I was speechless.

If I had taken a perfectionist decision, I wouldn’t have released this app. If I hadn’t spared the small amount of time to put this online, I wouldn’t have released this app. There were so many reasons not to release it, but I did it anyway, and it took only one person to make me appreciate my decision.

I have 3 other apps on the App Store alongside Angles in Photo: One app for cross-multiplication (yes, I made an app for that, and yes, people use it), another one for checking the glycemic index of a food (I was quite obsessed with my nutrition once, we’ve all been there), and another one that I built when Safari was getting very slow on my old iPhone so I decided to build the simplest web browser that doesn’t do 1000 things in the background. Some of them have advertisements to help me pay my bills, but they’re all free to use. People do use them, and people do give feedback, although I never show the care I’m supposed to – but that’s the whole point, and if I was obsessed with that, I wouldn’t have kept them online, and no one would be using them.

You might not always appreciate what you do, but that shouldn’t mean that other people won’t. If they won’t, you won’t lose anything, but if they do, then you realize that something that’s too little for you might mean a lot to someone else.

Oh, I had another feedback for Angles in Photo, saying “Helped me get through my divorce”. I can’t imagine how that happened, but, there you go!

Aligning SKPhysicsBody Correctly with SKShapeNode

When you want to create a circle in SpriteKit, SKShapeNode is the quickest and the simplest way for it. You don’t need to create images, you can manage the size, fill colour, stroke colours etc.

However, if you want this node to involve with collisions, you may get some trouble when creating an SKPhysicsBody for that node. The creating process is similar to SKSpriteNode but the alignment of it is different.

By default, SKShapeNode anchor points begin from (0, 0) and there isn’t any property to set it as we want. When we assign a SKPhysicsBody to this node, body’s anchor is its center point so the body doesn’t cover the node correctly.

Here’s how it looks when you want to implement for SKShapeNode as you do in SKSpriteNode:

skshapenode-skphysicsbody

There are plenty of people who were unable to solve this issue. Yet there is one answer on SO that helped me out with this.

To work around this situation, you can create an SKSpriteNode to implement the body and an SKShapeNode as the SKSpriteNode’s child to draw the shape.

First, create an SKSpriteNode and set the width and height same with the diameter of the circle

float radius = 30.0;
SKSpriteNode *aCircle = [SKSpriteNode spriteNodeWithColor:[UIColor clearColor] size:CGSizeMake(radius * 2, radius * 2)];

Then create a circular SKPhysicsBody for the SKSpriteNode (thankfully, this is possible)

SKPhysicsBody *circleBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
[circleBody setDynamic:NO];
[circleBody setUsesPreciseCollisionDetection:YES];
[aCircle circleBody];

Here is the key part. Create a CGPathRef as an ellipse in the rect with correct center values.

CGPathRef bodyPath = CGPathCreateWithEllipseInRect(CGRectMake(-[aCircle size].width / 2, -[aCircle size].height / 2, [aCircle size].width, [aCircle size].width), nil);
SKShapeNode *circleShape = [SKShapeNode node];
[circleShape setFillColor:[UIColor redColor]];
[circleShape setLineWidth:0];
[circleShape setPath:bodyPath];
[aCircle addChild:circleShape];
CGPathRelease(bodyPath);

Don’t forget to add the child to the scene then you are done. As you can see below, the physics body now matches the node frame correctly.

skspritenode-skshapenode-skphysicsbody.png

Source link of the answer:
SKPhysicsBody not as expected

Other questions about similar issues:
How to align SKPhysicsBody and SKShapeNode?
Align SKPhysicsBody with SKShapeNode
SKPhysicsBody does not work on SKShapeNode
SKshapenode is not responding to Physicsbody
how to make collisions with skshapenode circles
Keeping an SKShapeNode within the bounds of an SKSpriteNode

Sample Apple Push Notification PHP Script

This is an old script that I used to use for sending notifications. I don’t remember the source, on the other hand, it has been modified a couple of times already.

Even though I could manage to send notifications with this script, the Apple server was rejecting the connection after the amount reaches more than 50. It was usually getting cut off around 70. So it needs to get some management for not sending in a loop, like threading or whatever.

<?php
 
// set time limit to zero in order to avoid timeout
set_time_limit(0);
 
// charset header for output
header('content-type: text/html; charset: utf-8');
 
// this is the pass phrase you defined when creating the key
$passphrase = 'my_secret_pass';
 
// you can post a variable to this string or edit the message here
if (!isset($_POST['msg'])) {
$_POST['msg'] = "Notification message here!";
}
 
// tr_to_utf function needed to fix the Turkish characters
$message = tr_to_utf($_POST['msg']);
 
// load your device ids to an array
$deviceIds = array(
'lh142lk3h1o2141p2y412d3yp1234y1p4y1d3j4u12p43131p4y1d3j4u12p4313',
'y1p4y1d3j4u12p43131p4y1d3j4u12p4313lh142lk3h1o2141p2y412d3yp1234'
);
 
// this is where you can customize your notification
$payload = '{"aps":{"alert":"' . $message . '","sound":"default"}}';
 
$result = 'Start' . '<br />';
 
////////////////////////////////////////////////////////////////////////////////
// start to create connection
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'MyAppGenerated.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
 
echo count($deviceIds) . ' devices will receive notifications.<br />';
 
foreach ($deviceIds as $item) {
    // wait for some time
    sleep(1);
     
    // Open a connection to the APNS server
    $fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err, $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx);
 
    if (!$fp) {
        exit("Failed to connect: $err $errstr" . '<br />');
    } else {
        echo 'Apple service is online. ' . '<br />';
    }
 
    // Build the binary notification
    $msg = chr(0) . pack('n', 32) . pack('H*', $item) . pack('n', strlen($payload)) . $payload;
     
    // Send it to the server
    $result = fwrite($fp, $msg, strlen($msg));
     
    if (!$result) {
        echo 'Undelivered message count: ' . $item . '<br />';
    } else {
        echo 'Delivered message count: ' . $item . '<br />';
    }
 
    if ($fp) {
        fclose($fp);
        echo 'The connection has been closed by the client' . '<br />';
    }
}
 
echo count($deviceIds) . ' devices have received notifications.<br />';
 
// function for fixing Turkish characters
function tr_to_utf($text) {
    $text = trim($text);
    $search = array('Ü', 'Þ', 'Ð', 'Ç', 'Ý', 'Ö', 'ü', 'þ', 'ð', 'ç', 'ý', 'ö');
    $replace = array('Ãœ', 'Åž', '&#286;ž', 'Ç', 'Ä°', 'Ö', 'ü', 'ÅŸ', 'ÄŸ', 'ç', 'ı', 'ö');
    $new_text = str_replace($search, $replace, $text);
    return $new_text;
}
 
// set time limit back to a normal value
set_time_limit(30);