Swift: how to get decibels

You might had a chance to check out the new trend – scream application. The most popular one I believe “Chicken Scream”. The new weird and funny interface for the games. Let’s take a look at how chicken scream works – the main part of functionality is to detect the background noice to set up the threshold, and to get “loudness” value – some how to measure the decibels.
In scope of my tests and investigations I’ve decided to create a flappy bird like game but where you need to scream, kind of flappy bird scream, you may want check out the result – “Scream to Fly” (appstore link, my site link)

All right, the first thing to start you need to request access to the mic, to have such ability we need to add the following to the Info.plist – click “plus” and start typing “Privacy – Microphone Usage Description” and add the value, something like – “Access to microphone is needed to detect noice level only.”. This message will appear when you request the access to it programmatically.

To check whether permissions were granted or to request ones you’ll need the following method:

func permissionWasGranted(result: @escaping (_: Bool)->()) {
    switch AVAudioSession.sharedInstance().recordPermission() {
    case AVAudioSessionRecordPermission.granted:
        if IS_DEBUG { print("Permission granted") }
        result(true)
        return
    case AVAudioSessionRecordPermission.denied:
        if IS_DEBUG { print("Pemission denied") }
    case AVAudioSessionRecordPermission.undetermined:
        if IS_DEBUG { print("Request permission here") }
        AVAudioSession.sharedInstance().requestRecordPermission({ (granted) in
            if granted {
                result(true)
                return
            }
        })
    default:
        break
    }
    result(false)
}

In case of AVAudioSessionRecordPermission.denied user won’t be shown this request dialog anymore, the good practice is to detect this situation and show something on UI, just for instance – “you have disabled access to mic” and the screenshot how to enable it back in settings.

By the way, here’s the list of all imports you might need

import CoreAudio
import CoreAudioKit
import AVFoundation
import Foundation
import AVKit

Ok, let say user granted the access to the mic, now we are ready to start detecting the loudness, something like

audioService.permissionWasGranted(result: { granted in
    if (granted) {
        if IS_DEBUG { print("we have mic access!") }
        self.micOverlay.removeFromParent()
        self.audioService.initRecorder()
        self.audioService.start()
        self.audioService.startCalibration()
        self.initGame()
    } else {
        if IS_DEBUG { print("we don't have mic access :(") }
        self.micOverlay.render(rect: self.calculateOverlayRect(), text: "no mic access...")
        self.addChild(self.micOverlay)
    }
})

Let’s take a look at what is audioService and what initRecorder and start methods are doing

First of all, here’s the declaration of AudioService class and it’s protocols and some boilerplate

import CoreAudio
import CoreAudioKit
import AVFoundation
import Foundation
import AVKit
 
class AudioService : UIViewController, AVAudioRecorderDelegate {
 
    //...
 
    private var recorder : AVAudioRecorder? = nil
 
    func getDocumentsDirectory() -> URL {
        let fileManager = FileManager.default
        let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
        let documentDirectory = urls.first!
        return documentDirectory.appendingPathComponent("recording.m4a")
    }
 
 
    // MARK: protocol
 
 
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        recorder.stop()
        recorder.deleteRecording()
        recorder.prepareToRecord()
    }
}

Note, that you have to conform AVAudioRecorderDelegate protocol to be able to use AVAudioRecorder which will help us to get decibels.

Here’s how to initialize the AVAudioRecorder and AVAudioSession

func initRecorder() {
    let settings = [
        AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
        AVSampleRateKey: 12000,
        AVNumberOfChannelsKey: 1,
        AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
    ]
    do {
        if IS_DEBUG { print("init AVAudioRecorder...") }
        let session = AVAudioSession.sharedInstance()
        try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
        try session.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
        try session.setActive(true)
 
        try recorder = AVAudioRecorder(url: getDocumentsDirectory(), settings: settings)
        recorder!.delegate = self
        recorder!.isMeteringEnabled = true
        if !recorder!.prepareToRecord() {
            print("Error: AVAudioRecorder prepareToRecord failed")
        }
    } catch {
        print("Error: AVAudioRecorder creation failed")
    }
}

The most vital parts here are: set up AVAudioSession, init AVAudioRecorder with settings, reference recorder to our service class via delegate and run prepareToRecord. This initRecorder method should be called from “override func didMove(to view: SKView) {” in case is you use SKScene.
Now we are ready to call

func start() {
    recorder?.record()
    recorder?.updateMeters()
}

And this is it, the “tracker” is up and running. On every let say “override func update(_ currentTime: TimeInterval) {” we can get the decibel value with the following method

func update() {
    if let recorder = recorder {
        recorder.updateMeters()
    }
}
 
func getDispersyPercent() -> Float {
    if let recorder = recorder {
        let decibels = recorder.averagePower(forChannel: 0)
        //...
    }
    return 0
}

So, basically the answer is how to get decibels via AVAudioRecorder is to call recorder.averagePower(forChannel: 0) method. How to understand the value you will get?
this method will return Float value from -120 (the absolute quite) up to a 0 – hella loud.
In some cafe you will have something like -20 or even -16. So you can play with this values to experiment and find what you need. In my game I also have calibration – the first second I measure the background noice level – append all values into the array and try to calculate median and disperse to detect the “average” maximum of the background noice level to set up threshold correctly, so the game won’t react on a noice level.
Ping me in info (doggy) .com if any questions.
And feel free to enjoy
the result of these investigations .

This is it.

Social Share Toolbar

AWS DynamoDB query top 5 scores for country

Let’s take a look at how to query top 5 scores from AWS DynamoDB, let say we have some top scores table and want to get top 5 performers for the specific country. What we also need is not to waste the network resources, that’s why we will specify what exact fields we need to fetch from server – with help of projectionExpression.
Moreover let’s take a look at how to deal with the following error:
com.amazon.coral.validate#ValidationException”,”message”:”Invalid ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: name”

it could be solved by adding reserved words to the expressionAttributeNames, e.g. instead of

queryExpression.projectionExpression = "userId,challenge,country,name,total"

where name and total are reserved words let’s do the following

queryExpression.expressionAttributeNames = [
            "#n": "name",
            "#t": "total",
        ]
queryExpression.projectionExpression = "userId,challenge,country,#n,#t"

That approach will eliminate the ValidationException – reserved keyword; reserved keyword error.

Here’s the whole method
Continue reading “AWS DynamoDB query top 5 scores for country”

Social Share Toolbar

Swift: How to create iOS Today extension and share data with containing app

Do you happen had a chance to see the 3d touch icon widget or iOS Today Extension, let’s take a look at how to create an Extension in swift, how to open containing app and how to share data with containing app.

To add Today Extension to the project click “file” -> “new” -> “target”: chose “Today Extension”.
Let’s go to “Capabilities” tab and enable “App Groups” for both AppWithExtension and Extension targets, for both app and extension you need to click “+” icon under the “App Groups” and add Groups to you app id, e.g. like here


Continue reading “Swift: How to create iOS Today extension and share data with containing app”

Social Share Toolbar

Swift: how to use 3D Touch – Introduction

3D Touch or force touch – is a new feature that have been introduced in the iPhone 6s. It’s interesting to know that you can get touch force, it is a number from 0 up to 6.6667, actually it a number from 0 up to maximumPossibleForce.

Let’s take a look at how to check if 3d touch is supported on current device:

func is3dTouchAvailable(traitCollection: UITraitCollection) -> Bool {
    return traitCollection.forceTouchCapability == UIForceTouchCapability.available
}
 
// Let's call it somewhere from the GameScene for instance
override func didMove(to view: SKView) {
 
    hardwareService.initDevice()
    if(is3dTouchAvailable(traitCollection: self.view!.traitCollection)) {
       //...
    }
 
}

Feel free to take a look at the example project – I’ll provide the link in the end of this post.
So this is a good practice to check whether 3d touch is supported or not.

Here’s an example of how to get the force of the 3d touch

func touchMoved(touch: UITouch, toPoint pos: CGPoint) {
    let location = touch.location(in: self)
    let node = self.atPoint(location)
 
    //...
    if is3dTouchEnabled {
        bubble.setPressure(pressurePercent: touch.force / touch.maximumPossibleForce)
    } else {
        // It is important to use touchMoved for iPhones w/o 3dTouch,
        // So we can "emulate" it.
        bubble.setPressure(pressurePercent: 1)
    }
}

The current value of the Touch’s force is touch.force and the touch.maximumPossibleForce is a good one to convert force value to the “percentage”.
This code is from the small example of how to “emulate” the bubble reacting on the force of the touch.
https://github.com/DjComandos/3dTouch
To try this on your iPhone 6s/7 you can install this bubble wrap app real quick.

Here is my previous post with more details about Haptic feedback.

Social Share Toolbar

IoT: raspberry pi 3 how to blink led from iPhone

Internet of Things: raspberry pi 3 – how to blink led from iPhone?

Here’s a tech stack:
– raspberry pi 3 + led
– node-red hosted in AWS/anywhere – should be available in internet
– node js script on raspberry pi connected to node-red via web socket
– swift/ios app which makes http requests to node-red

Let’s start from node-red configuration (it’s a really cool tool for wiring together hardware devices, APIs and online services). All we need is to have “http GET” input, let specify the following path “/mmled/status” for it. and we want to wire it with Output/”Listen on” “web socket” with “/ws/mmled” path. We might also want to add “pay load” node.
So with several “debug” nodes (convenient way to debug our endpoint) it should look something like this:

nodered

At this point if we will GET https://{nore-red-host}/mmled/status?value=1 we will see the following things:
– {“value”: 1} in the debug panel
– the same message we will get in case if we are listening the “wss://{nore-red-host}/ws/mmled” socket.

Let’s check it, here is the node js “server” script which we going to start like “sudo node server” on raspberry pi:

var W3CWebSocket = require('websocket').w3cwebsocket;
var Gpio = require('onoff').Gpio;
var led = new Gpio(17, 'out');  
var client = new W3CWebSocket('wss://{node-red-host}/ws/mmled');
 
client.onerror = function() {
    console.log('Connection Error');
};
 
client.onopen = function() {
    console.log('WebSocket Client Connected');
};
 
client.onclose = function() {
    console.log('echo-protocol Client Closed');
    if (led) led.unexport();
};
 
client.onmessage = function(e) {
    if (typeof e.data === 'string') {
        console.log("Received: '" + e.data + "'");
        try {
            var data = JSON.parse(e.data);
            var newValue = parseInt(data.value);
            console.log('got new value = ' + newValue);
            led.writeSync(newValue);
        } catch (ex) {
            console.log('invalid JSON in e.data');
        }
    }
};

Ok, let’s digg into the magic, first of all what is

var Gpio = require('onoff').Gpio;
var led = new Gpio(17, 'out');

if you will google “raspberry pi 3 pins” you’ll find where “GPIO 17” – you basically need to connect it to resistor + led + GND
after that you’ll have a chance to turn led on with this line – “led.writeSync(1);” and turn it off with “led.writeSync(0);“. So the circuit is the following:
raspberry pi [DPIO 17 pin] -> resistor -> led -> raspberry pi [GND pin]

Everything else we need to connect and listen socket and to react on the value change. run the script with

sudo node server

and try to GET https://{node-red-host}/mmled/status?value=1
and and try to GET https://{node-red-host}/mmled/status?value=0 to toggle the led. It’s awesome, at this step you can enable/disable your LED from internet!

Let’s create a new “single view” xcode project and drop a “switch” UI element on the view. Go to “Assistant editor” and bind the switch’s “action” to the code. Here is the controller’s code:

import UIKit
 
class ViewController: UIViewController {
 
    let ledUrl = "https://{node-red-host}/mmled/status"
 
    func setLed(status: Bool) {
        let finalUrl = ledUrl + "?value=" + (status ? "1" : "0")
        print("final url = " + finalUrl)
        let nsUrl = NSURL(string: finalUrl)
        let task = NSURLSession.sharedSession().dataTaskWithURL(nsUrl!) {(data, response, error) in
            print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        }
 
        task.resume()
    }
 
    @IBAction func onChange(sender: UISwitch) {
        setLed(sender.on);
    }
 
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }
 
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

It’s just sending GET request with the value 1 or 0 on toggle/switch change. That’s it, if you’ll run the app and toggle the switch you’ll see the message in the “debug” node, and you’ll see your LED blinking which is Awesome! You might go further and replace led with relay+real lamp, but it’s a different story.

Here’s an EPIC demo video, just for fun and self-motivation.

Social Share Toolbar

WatchOS 2 tutorial: Activity Ring – WKInterfaceActivityRing

Just a quick example how to use WKInterfaceActivityRing and HKActivitySummary to show Activity Ring.

Simulator Screen Shot Jun 7, 2016, 8.58.27 PM

Go ahead and drag “Activity Ring” on your Interface Controller, use Assistant Editor to add Outlet to your Controller, e.g.

@IBOutlet var activityRing: WKInterfaceActivityRing!

All you need to set up Activity Ring values is to create and initialize HKActivitySummary, here is the sample code (for simplicity we are going to use the same values for all the rings)

let summary = HKActivitySummary()
let value: Double = 2
let goal: Double = 10
 
summary.activeEnergyBurned = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: value)
summary.activeEnergyBurnedGoal = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: goal)
 
summary.appleExerciseTime = HKQuantity(unit: HKUnit.minuteUnit(), doubleValue: value)
summary.appleExerciseTimeGoal = HKQuantity(unit: HKUnit.minuteUnit(), doubleValue: goal)
 
summary.appleStandHours = HKQuantity(unit: HKUnit.countUnit(), doubleValue: value)
summary.appleStandHoursGoal = HKQuantity(unit: HKUnit.countUnit(), doubleValue: goal)
 
activityRing.setActivitySummary(summary, animated: true)

That’s pretty much it.

Social Share Toolbar

Watch OS 2: Refreshing Apple Watch complication

To make WCSession work fine with Complications we have to use transferCurrentComplicationUserInfo on the iPhone side.
So here’s how to send updated dictionary from iPhone

// update complication
if session.watchAppInstalled {
    session.transferCurrentComplicationUserInfo(dataDictionary)
}

Let’s get back to Apple Watch Extension, here’s what should be in the InterfaceController which uses WCSession to subscribe on updates:

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]) {
    if let dateString = userInfo["dataKey"] as? String {
         // save new value to user defaults
         let defaults = NSUserDefaults.standardUserDefaults()
         defaults.setObject(dateString, forKey: "dataKey")
 
         // reload complication data
         reloadComplications()
     }
 }
 
func reloadComplications() {
    let server = CLKComplicationServer.sharedInstance()
    guard let complications = server.activeComplications where complications.count > 0 else {
        return
    }
 
    for complication in complications  {
        server.reloadTimelineForComplication(complication)
    }
}

That’s basically it, now we can get these values from the Cache in the Complication itself.

Social Share Toolbar

watchOS 2: Writing a WatchKit Complication

How to create custom WatchKit Complication which could be easily added to Apple Watch face.
This is one of the extremely exciting new additions to the WatchKit Framework in watchOS 2 – ability to add custom complications to the clock faces provided by Apple.

First let’s add Complication Data Source

import ClockKit
 
class Complication: NSObject, CLKComplicationDataSource {
 
    func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
        handler(nil)      
    }
 
    func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
        handler(nil)
    }
 
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.ShowOnLockScreen)
    }
 
    func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler([])
    }
 
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler([CLKComplicationTimeTravelDirections.None])        
    }
 
    func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
 
    func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
}

Let’s register it in the info.plist, make sure, that the following properties are set correctly
CLKComplicationPrincipalClass set to e.g. “$(PRODUCT_MODULE_NAME).Complication”
CLKComplicationSupportedFamilies has at least one value – CLKComplicationFamilyCircularSmall
RemoteInterfacePrincipalClass set to e.g. $(PRODUCT_MODULE_NAME).InterfaceController

Let’s update our data source template to provide some percentage like data, let say we have goal of 10 something and right now our progress is 8/10 – let’s render it (let’s make data static not to overcomplicate the example):

import ClockKit
 
class Complication: NSObject, CLKComplicationDataSource {
 
    func getNextRequestedUpdateDateWithHandler(handler: (NSDate?) -> Void) {
        // Update hourly
        handler(NSDate(timeIntervalSinceNow: 60*60))
    }
 
    func getPlaceholderTemplateForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTemplate?) -> Void) {
        var template: CLKComplicationTemplate? = nil
        switch complication.family {
        case .ModularSmall:
            template = nil
        case .ModularLarge:
            template = nil
        case .UtilitarianSmall:
            template = nil
        case .UtilitarianLarge:
            template = nil
        case .CircularSmall:
            let modularTemplate = CLKComplicationTemplateCircularSmallRingText()
            modularTemplate.textProvider = CLKSimpleTextProvider(text: "--")
            modularTemplate.fillFraction = 0.7
            modularTemplate.ringStyle = CLKComplicationRingStyle.Closed
            template = modularTemplate
        }
        handler(template)
    }
 
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.ShowOnLockScreen)
    }
 
    func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void) {
        if complication.family == .CircularSmall {
            // these values should be fetched from somewhere...
            let progress = 8
            let goal = 10
 
            let template = CLKComplicationTemplateCircularSmallRingText()
            template.textProvider = CLKSimpleTextProvider(text: "\(progress)")
            template.fillFraction = Float(progress) / Float(goal)
            template.ringStyle = CLKComplicationRingStyle.Closed
 
            let timelineEntry = CLKComplicationTimelineEntry(date: NSDate(), complicationTemplate: template)
            handler(timelineEntry)
        } else {
            handler(nil)
        }
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler(nil)
    }
 
    func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void) {
        handler([])
    }
 
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler([CLKComplicationTimeTravelDirections.None])
    }
 
    func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
 
    func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        handler(NSDate())
    }
}

Feel free to use transferCurrentComplicationUserInfo for getting actual data, or simply get the data from NSCache().

Social Share Toolbar

swift: How to render a UIView to a UIImage

You can render any UIView into UIImage in just several lines of code. Never mind subviews – that will be handled automatically. Here’s the code:

 
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, true, 0)
    view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

For sure you can extend UIImage for convenience

import UIKit
 
 
extension UIImage {    
    convenience init(view: UIView) {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, true, 0)
        view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(CGImage: image.CGImage!)
    }
}

After that you’ll need just:

 
    UIImage(view: someChartView)

That’s it!

P.S. Available from iOS 7.0

Social Share Toolbar