<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Tsutsumi Shuichi on Medium]]></title>
        <description><![CDATA[Stories by Tsutsumi Shuichi on Medium]]></description>
        <link>https://medium.com/@shu223?source=rss-91f89751713------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*h5lHyG73_rAaByou7Ci3CA.png</url>
            <title>Stories by Tsutsumi Shuichi on Medium</title>
            <link>https://medium.com/@shu223?source=rss-91f89751713------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 10:11:01 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@shu223/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Browse 1,000 photos in 10 seconds in an iOS app]]></title>
            <link>https://medium.com/@shu223/browse-1-000-photos-in-10-seconds-in-an-ios-app-489231ffad7d?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/489231ffad7d</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ui]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[photos]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Sun, 29 Aug 2021 03:26:23 GMT</pubDate>
            <atom:updated>2021-08-29T03:26:23.337Z</atom:updated>
            <content:encoded><![CDATA[<h3>I never have time to look back the lots of pictures…</h3><p>With this in mind, I created an iOS app called “SOMATO” that allows us to browse 100 photos in a second at maximum.</p><p>If you have 1,000 photos, you can view them in 10 seconds.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FHJnD2gubRHk%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DHJnD2gubRHk&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FHJnD2gubRHk%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/022858c851a3c124a0365846aa0168a2/href">https://medium.com/media/022858c851a3c124a0365846aa0168a2/href</a></iframe><p>Well, to be honest, the 100 images/second speed is more for impact than anything else, but surprisingly, even when the speed is around 20 images/second, you can still feel nostalgic and soak in the memories. It’s free, so if you’re interested, please download it and give it a try.</p><p><a href="https://apps.apple.com/app/id1566199421">‎High Speed Photo Album SOMATO</a></p><p>(Available on iPhone, iPad, and M1 Mac)</p><p>This article describes the implementation of this application.</p><h3>Overview</h3><p>In fact, Somato is not particularly technically advanced.</p><p>To put it bluntly, <strong>it just acquires images with Photos (PhotoKit) and displays them with UICollectionView</strong>.</p><p>The first prototype took less than an hour to build. I didn’t have to do anything special, just a straightforward implementation, and I was like, “Hey, it’s displaying in less than 1/100th of a second…! That’s what I thought. (When I made an app with a similar concept about 10 years ago, it didn’t work like this due to the limitations of device performance.</p><p>But of course, <strong>it was not so easy when it came to making a proper product</strong>…</p><h3>Memory Issues</h3><p>In the first prototype I made, I loaded all the 500 images I had in my iPhone as UIImage first (before starting to display them).</p><p>Since the size of the images should be large enough to be displayed in three rows (375x375 on an iPhone 12 mini), the memory usage was still not significant.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sUCCu42eMh790msOiHPAeA.jpeg" /></figure><p>However, this is a different story when you have more than 10,000 images on your iPhone. In fact, the designer I worked with had 16,000 photos and videos on his iPhone. <strong>No matter how small the size, 10,000 images cannot be stored in memory as UIImage.</strong></p><p>Furthermore, a single row display would require higher resolution image data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ANDzvmRPCVdTlJ0Z7wGQ2A.jpeg" /></figure><p>The concept of the app is to be able to look back at a large number of photos at high speed, so I can’t just set a limit of “up to 1,000 albums”. <strong>10,000 or 1,000,000 photos need to be able to be displayed at high speed without eating up memory.</strong></p><h3>Multi-threaded problem</h3><p>Due to the above limitation of memory usage, it is <strong>necessary to cache a certain number of images in memory and display them, while discarding the old cache and reading them in the background thread</strong> at a pace of 1/100th of a second.</p><p>So, in addition to the main thread for drawing the screen, multi-threaded processing is required: a thread for managing the memory cache, a thread for retrieving images from local or iCloud, and so on.</p><p>It is also important to note that the cache should be in a <strong>Serial Queue</strong> to unify access, and the image acquisition should be in a <strong>Concurrent Queue</strong> for parallel processing.</p><h3>The problem of video frame extraction processing</h3><p>Since the concept is to “browse a large number of photos at high speed”, we also want to look back at videos at high speed, so Somato <strong>extracts a frame from the video every n seconds</strong> and plays it at high speed just like a single photo. (This is quite emotive.)</p><p>This video processing is even more difficult, because <strong>the process of extracting frames involves video decoding</strong>, so no matter how much you read ahead while displaying 1/100th of a second, you will not be able to finish in time.</p><p>Therefore, it is necessary to cache the extracted frames, and then load the necessary portions into memory while discarding them.</p><h3>The Problem with iCloud</h3><p>iCloud was even more egregious. There were two main problems</p><ol><li>Problems with the <strong>loading speed from iCloud</strong></li><li><strong>PhotoKit’s Internal Error</strong> problem</li></ol><p>The first was a problem with downloading over the network.</p><p>For the first one, when downloading over the network, it naturally takes much longer than loading from the local network, so when doing PHImageManager.default().requestImage, <strong>first getting low-resolution thumbnail-level images</strong> with:</p><pre>let options = PHImageRequestOptions()<br>options.deliveryMode = .fastFormat</pre><p><strong>then getting the necessary size images from iCloud</strong> and caching the files. These are done in a background thread while playing at 100 images/second.</p><p>However, <strong>the more complicated and inevitable problem for third-party developers</strong> is #2. This is caused by the iCloud sync status of your system. If you go to the Settings app, turn off Photos &gt; iCloud Photos, wait 5 minutes (after the data is deleted), turn it back on, and then go to the standard Photos app, you will see that iCloud shared albums and other photos do not appear.</p><p>Immediately after enabling iCloud sync, or when you haven’t viewed the iCloud album (photos) in question for a long time in the standard Photos app, the OS hasn’t even been able to acquire thumbnail resolution images yet, and if you try to access the photos in the Photos framework at this time, it will cause the Internal Error.</p><pre>[PhotoKit] Error: Unknown internal error</pre><h3>Conclusion</h3><p>I wrote about the implementation of a viewer application that displays photos at high speed.</p><p>It was “easy to make” with about 500 photos, but when I started to consider about 16,000 photos, video support, iCloud support, etc., it was unexpectedly difficult.</p><p>There may not be many cases where you want to display images at 1/100th of a second, but</p><ul><li>Fetching images with Photos at high speed</li><li>To save memory (not proportional to the number of images)</li><li>Extracting frames from a video at high speed</li></ul><p>I think this kind of implementation itself is actually rather useful in general.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=489231ffad7d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to use USDZ files with ARKit / SceneKit]]></title>
            <link>https://medium.com/@shu223/how-to-use-usdz-files-with-arkit-scenekit-d2615bbb9963?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/d2615bbb9963</guid>
            <category><![CDATA[arkit]]></category>
            <category><![CDATA[3d]]></category>
            <category><![CDATA[usd]]></category>
            <category><![CDATA[scenekit]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Sat, 08 Sep 2018 01:37:17 GMT</pubDate>
            <atom:updated>2018-09-25T22:29:25.705Z</atom:updated>
            <content:encoded><![CDATA[<p>The .usdz format is a “universal” format, but the <a href="https://developer.apple.com/videos/play/wwdc2018/603/">WWDC18 session</a> does NOT show the concrete way to load 3D models from usdz files in ARKit or SceneKit projects.</p><p>Here is the way:</p><pre>guard let url = Bundle.main.url(/service/forresource: name, withExtension: &quot;usdz&quot;) else { fatalError() }<br>let mdlAsset = MDLAsset(url: url)<br>let scene = SCNScene(mdlAsset: mdlAsset)</pre><p>That’s it. Anything is not special for the `usdz` format.</p><p>You will need</p><pre>import SceneKit.ModelIO</pre><p>to initialize SCNScene from MDLAssset.</p><p>This implementation is available on <strong>iOS 10 or greater</strong> because USD format has been supported since iOS 10. You don’t need to wait for iOS 12.</p><p>MDLAsset currently supports following formats:</p><ul><li>.abc</li><li>.usd .usda .usdc</li><li>.usdz</li><li>.ply</li><li>.obj</li><li>.stl</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d2615bbb9963" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Core Image filters with Metal]]></title>
            <link>https://medium.com/@shu223/core-image-filters-with-metal-71afd6377f4?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/71afd6377f4</guid>
            <category><![CDATA[image-processing]]></category>
            <category><![CDATA[metal]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Sun, 10 Dec 2017 12:57:05 GMT</pubDate>
            <atom:updated>2017-12-10T23:34:10.830Z</atom:updated>
            <content:encoded><![CDATA[<p>We can create custom Core Image filters which is written in Metal Shading Language (MSL) in iOS 11 or greater.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mmXeAWbq_B80KALcS2CX1g.jpeg" /><figcaption><a href="https://developer.apple.com/videos/play/wwdc2017/510/">Advances in Core Image: Filters, Metal, Vision, and More — WWDC 2017 — Videos — Apple Developer</a></figcaption></figure><p>It’s supported on <strong>A8 or newer</strong> devices.</p><p>It can achieve better performances because it’s pre-compiled at build-time. Existing way (GLSL or CIKernel Language) are compiled at run-time.</p><h3>How to implement?</h3><p>Followings are the new APIs added to CIKernel:</p><pre>public convenience init(functionName name: String, fromMetalLibraryData data: Data) throws</pre><pre>public convenience init(functionName name: String, fromMetalLibraryData data: Data, outputPixelFormat format: CIFormat) throws</pre><p>Here is a simple MSL implementation for CIColorKernel:</p><pre>#include &lt;metal_stdlib&gt;<br>using namespace metal;<br>#include &lt;CoreImage/CoreImage.h&gt; // includes CIKernelMetalLib.h<br><br>extern &quot;C&quot; { namespace coreimage {<br><br>    float4 myColor(sample_t s) {<br><br>        return s.grba;<br>    }<br><br>}}</pre><p>Let’s save it as kernel.metal or something (Any name is OK, but the extention should be .metal`).</p><p>A CIColorKernel which uses the shader function can be initialized using the new API:</p><pre>let url = Bundle.main.url(/service/forresource: &quot;default&quot;, withExtension: &quot;metallib&quot;)!<br>let data = try! Data(contentsOf: url)<br>let kernel = try! CIColorKernel(functionName: &quot;myColor&quot;, fromMetalLibraryData: data)</pre><p>So, a CIFilter subclass which has the custom kernel can be implemented like this:</p><pre>import CoreImage<br><br>class MetalFilter: CIFilter {<br><br>    private let kernel: CIColorKernel<br><br>    var inputImage: CIImage?<br><br>    override init() {<br>        let url = Bundle.main.url(/service/forresource: &quot;default&quot;, withExtension: &quot;metallib&quot;)!<br>        let data = try! Data(contentsOf: url)<br>        kernel = try! CIColorKernel(functionName: &quot;myColor&quot;, fromMetalLibraryData: data)<br>        super.init()<br>    }<br><br>    required init?(coder aDecoder: NSCoder) {<br>        fatalError(&quot;init(coder:) has not been implemented&quot;)<br>    }<br><br>    func outputImage() -&gt; CIImage? {<br>        guard let inputImage = inputImage else {return nil}<br>        return kernel.apply(extent: inputImage.extent, arguments: [inputImage])<br>    }<br>}</pre><p>The custom filter can be used like this:</p><pre>let filter = MetalFilter()<br>filter.inputImage = inputImage<br>let outputImage = filter.outputImage()</pre><p>That’s it!</p><h3>Build Settings</h3><p>OK, now we can build the project. Let’s run it on your device!</p><pre>+[CIKernel kernelWithFunctionName:fromMetalLibraryData:outputPixelFormat:error:] Function &#39;myColor&#39; does not exist.</pre><p>Hmm, but it says myColor does not exist. But it exists…</p><p>I found the solution here:</p><ul><li><a href="https://developer.apple.com/documentation/coreimage/cikernel/2880194-kernelwithfunctionname">kernelWithFunctionName:fromMetalLibraryData:error: — CIKernel | Apple Developer Documentation</a></li></ul><p>We need to specify some options in the build settings:</p><ul><li>Specify -fcikernel flag in the Other Metal Compiler Flags option</li><li>Add a user-defined setting with a key called MTLLINKER_FLAGS with a value of -cikernel</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1008/1*bszIthvub9W720di9PTdMw.png" /></figure><h3>It works!</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JkOHPBdVbfm6d3f8Em9Seg.jpeg" /><figcaption>(Left: Original, Right: Filter Applied)</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71afd6377f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Model format of MPSCNN on iOS (Metal Performance Shaders)]]></title>
            <link>https://medium.com/@shu223/model-format-of-mpscnn-on-ios-metal-performance-shaders-94b653a3dcc8?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/94b653a3dcc8</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[tensorflow]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Sun, 16 Apr 2017 01:20:35 GMT</pubDate>
            <atom:updated>2017-04-18T01:24:42.262Z</atom:updated>
            <content:encoded><![CDATA[<p>In iOS 10, API to implement and run Convolutional Neural Network (CNN) on iOS devices have been added to the MetalPerformanceShaders (MPS) framework. <strong>We can now take advantage of GPU on iOS devices to run fast CNN computation</strong>. In other word, we can use outcome of cutting edge deep learning technologies on your device even while offline.</p><p>Apple provides samples for the MPSCNN (<a href="https://developer.apple.com/library/content/samplecode/MPSCNNHelloWorld/Introduction/Intro.html">MPSCNNHelloWorld</a> / <a href="https://developer.apple.com/library/content/samplecode/MetalImageRecognition/Introduction/Intro.html#//apple_ref/doc/uid/TP40017385">MetalImageRecognition</a>), and I integrated it into my sample code collection repo:</p><p><a href="https://github.com/shu223/iOS-10-Sampler">shu223/iOS-10-Sampler</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*YAqDD4CGAIy-WYx8HITPPA.gif" /><figcaption>Realtime Image Recognition using MPSCNN</figcaption></figure><p>In these sample apps, <strong>pre-trained model parameters</strong> (weight / bias) are used in the app, and those files are named <strong>xxxx.dat</strong>.</p><p><strong>What kind of file is this?</strong> Is this something special by Apple? Can MPS CNN use only this format? What format is the content?</p><p>In this article, I write about the model format of MPS CNN.</p><h3>File format</h3><p>The `xxxx.dat` files which are included in samples such as Apple’s MPSCNNHelloWorld or MetalImageRecognition are <strong>ordinary binary files</strong>. Thus those are <strong>NOT</strong> MPSCNN or Apple proprietary / exclusive.</p><p>This file can be loaded to memory <strong>without any dependencies on Metal or MetalPerformanceShaders</strong> as follows:</p><pre>let fd = open( filePath, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)<br>assert(fd != -1, “Error: failed to open output file at \””+filePath+”\” errno = \(errno)\n”)</pre><pre>guard let hdr = mmap(nil, size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0) else {<br>    close(fd)<br>    return nil<br>}</pre><p>Then, to use this with MPSCNN,</p><p>Casting UnsafeMutableRawPointer to UnsafePointer&lt;Float&gt;,</p><pre>let p = UnsafePointer&lt;Float&gt;(hdr.assumingMemoryBound(to: Float.self))<br>assert(p != UnsafePointer&lt;Float&gt;(bitPattern: -1), “mmap failed with errno = \(errno)”)</pre><p>and pass this pointer to the initializer of MPSCNNConvolution or MPSCNNFullyConnected.</p><pre>public init(device: MTLDevice, convolutionDescriptor: MPSCNNConvolutionDescriptor, kernelWeights: UnsafePointer&lt;Float&gt;, biasTerms: UnsafePointer&lt;Float&gt;?, flags: MPSCNNConvolutionFlags)</pre><p>MPSCNNConvolution or MPSCNNFullyConnected copies the parameters when it’s initialized, so you can release the data loaded on memory and close the file.</p><pre>munmap(hdr, Int(size))<br>close(fd)</pre><p>These are also independent of Metal or MetalPerformanceShaders, very generic methods for files or memory.</p><p>In conclusion, <strong>any file format can be used</strong>, as far as it can be read by the iOS app.</p><h3>Which tools can be used to train the models?</h3><p>In Apple’s MPSCNNHelloWorld and MetalImageRecognition, they say the parameters are trained using Google’s <strong>TensorFlow</strong>.</p><p><a href="http://qiita.com/makomori_q/items/dc31788d2a8a854b1ad6#_reference-aea040b9d41f39dbecde">Another article</a> says they used <strong>Chainer</strong> or <strong>Kelas</strong> for the training, and ported the parameters to iOS using a file format called <strong>HDF5</strong>.</p><h3>Weight Ordering</h3><p>Although any file format can be used for the parameter files, there is a rule for the content.</p><p>Let’s look at the initializer of MPSCNNConvolution again:</p><pre>public init(device: MTLDevice, convolutionDescriptor: MPSCNNConvolutionDescriptor, kernelWeights: UnsafePointer&lt;Float&gt;, biasTerms: UnsafePointer&lt;Float&gt;?, flags: MPSCNNConvolutionFlags)</pre><p>The weights in CNN should be a 4D tensor (Input Channels, Kernel Width, Kernel Height, Output Channels), but there is <strong>no argument to tell the order of the 4D tensor</strong> in the initializer.<br> <br>So, <strong>we have to pass the weights 4D tensor with following order</strong>:</p><pre>weight[ outputChannels ][ kernelHeight ][ kernelWidth ][ inputChannels ]</pre><p>For example, to read it with for loop, the code is like this:</p><pre>for o in 0..&lt;outputChannels {<br>    for ky in 0..&lt;kernelHeight {<br>        for kx in 0..&lt;kernelWidth {<br>            for i in 0..&lt;inputChannels {<br>                let index = Int(((o * kernelHeight + ky) * kernelWidth + kx) * inputChannels + i)<br>                print(String(format: &quot;\(index): %.3f&quot;, kernelWeights[index]))<br>            }<br>        }<br>    }<br>}</pre><p>(reference: <a href="http://stackoverflow.com/questions/40522224/mpscnn-weight-ordering">MPSCNN Weight Ordering — Stack Overflow</a>)</p><h3>Exporting from TensorFlow</h3><pre>import tensorflow as tf</pre><pre>// …</pre><pre>w_conv1 = weight_variable([5, 5, 1, 32])<br>b_conv1 = bias_variable([32])</pre><pre>// …(train)</pre><p>Assume that you get trained weights/bias parameters using TensorFlow with above code.</p><p>To export to files named “weights_xxx.dat” or “bias_xxx.dat” respectively to use those in MPSCNN, you can do it as follows:</p><pre>with open(&#39;weights_conv1.dat&#39;, &#39;w&#39;) as f:<br>    w_conv1_p = tf.transpose(w_conv1, perm=[3, 0, 1, 2])<br>    f.write(session.run(w_conv1_p).tobytes())<br>with open(&#39;bias_conv1.dat&#39;, &#39;w&#39;) as f:<br>    f.write(session.run(b_conv1).tobytes())</pre><p>As for weights for Fully-Connected layer, the code will be as follows:</p><pre>with open(&#39;weights_fc1.dat&#39;, &#39;w&#39;) as f:<br>    w_fc1_shp = tf.reshape(w_fc1, [7, 7, 64, 1024])<br>    w_fc1_p = tf.transpose(w_fc1_shp, perm=[3, 0, 1, 2])<br>    f.write(session.run(w_fc1_p).tobytes())The points are:</pre><ul><li>Open the file using `open()` as “write” mode.</li><li>Re-order the weights 4D tensor.</li><li>As for a fully connected layer, reshape the 2D tensor to 4D, then transpose it.</li><li>Write the binary data to the file using `write()` or `tobytes()`.</li></ul><h3>Wrap up</h3><ul><li>Any file format can be used, as far as it can be read by the iOS app.</li><li>The order of the trained params’ 4D tensor should be:</li></ul><pre>weight[ outputChannels ][ kernelHeight ][ kernelWidth ][ inputChannels ]</pre><ul><li>As far as conditions above are satisfied, any tools / libraries can be used such as TensorFlow, Chainer, etc.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*nbXMexCVMcNfgbdW1u_p1w.gif" /><figcaption>Swift Logo Detection using MPSCNN</figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=94b653a3dcc8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pulse animations with 3 lines on iOS]]></title>
            <link>https://medium.com/@shu223/display-pulse-animations-with-3-lines-on-ios-4de057647936?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/4de057647936</guid>
            <category><![CDATA[bluetooth]]></category>
            <category><![CDATA[beacons]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ibeacon]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Tue, 12 Apr 2016 23:00:32 GMT</pubDate>
            <atom:updated>2017-04-18T01:23:46.927Z</atom:updated>
            <content:encoded><![CDATA[<p>“<a href="https://github.com/shu223/pulsator"><strong>Pulsator</strong></a>” is an OSS which enable to add pulse animations easily for iOS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*5Uqc21Z2pC2ysftmCKvb5A.gif" /><figcaption>PulsatorDemo</figcaption></figure><p><a href="https://github.com/shu223/pulsator"><strong>https://github.com/shu223/pulsator</strong></a></p><p>It’s good for indicating:</p><ul><li>locations on map</li><li>beacons of iBeacon</li><li>Bluetooth Low Energy (BTLE) devices</li><li>or other “Radio Wave” related devices</li></ul><p>It can be installed in an instant, so I use it on not only my client works, but also small prototypes, or works in hackathons.</p><p>It’s written in <strong>Swift</strong>, and the license is MIT.</p><h3>How to use?</h3><p>It can be used with only <strong>3 lines of code</strong>.</p><h4>1. Initialize</h4><pre>let pulsator = Pulsator()</pre><h4>2. Add to your view’s layer</h4><pre>view.layer.addSublayer(pulsator)</pre><h4>3. Start animating</h4><pre>pulsator.start()</pre><h3>Customizations / Demo</h3><p>You can customize the radius, color, number of pulses, etc. Please refer to the demo project or Playground which are in the repository.</p><h4>Demo</h4><ul><li>Example/PulsatorDemo.xcodeproj</li></ul><h4>Playground</h4><ul><li>Pulsator.playground</li></ul><h4>Browser Demo</h4><p>You can try also on <strong>browsers</strong>.</p><p><a href="https://appetize.io/app/45kwjngp1xud45eeqhxqy8qqew?device=iphone5s&amp;scale=75&amp;orientation=portrait&amp;osVersion=9.3"><strong>PulsatorDemo</strong></a></p><h3>Installation</h3><p>It supports CocoaPods and Carthage.</p><h4>CocoaPods</h4><p>Add into your Podfile.</p><pre>pod “Pulsator”</pre><h4>Carthage</h4><p>Add into your Cartfile.</p><pre>github “shu223/Pulsator”</pre><h3>Showcase</h3><p>I used the Objective-C version of this library “<a href="https://github.com/shu223/PulsingHalo">PulsingHalo</a>” in many client works, open source projects and so on.</p><h4><a href="http://whill.us/">WHILL</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*nJQux8SKh0SY3Er95YkCZg.gif" /><figcaption>WHILL Remote Control</figcaption></figure><h4><a href="http://www.moff.mobi/">Moff</a></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*FQecFXV3wxxUCqyJWj4xsQ.gif" /><figcaption>Moff</figcaption></figure><h4><a href="https://bonx.co/">BONX</a></h4><p>Waves which indicate “talking”</p><h4><a href="https://github.com/shu223/iOS-9-Sampler">iOS-9-Sampler</a></h4><p>Indicates the locations and strengths of forth touches.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/200/1*I0D0Wut87qN_Fco1vHb72Q.gif" /></figure><h3>Inside of Pulsator</h3><p>Pulsator is implemented using Core Animation.</p><h4>CAReplicatorLayer</h4><p>Pulsator is a subclass of CAReplicatorLayer.</p><pre>public class Pulsator: CAReplicatorLayer</pre><p>CAReplicatorLayer is a layer which creates `instanceCount` copies of its sublayers, so Pulsator uses it to generate <strong>multiple pulses</strong>.</p><h4>CAAnimationGroup</h4><p>The animation for each pulse consists of <strong>scale animation + opacity animation,</strong> and Pulsator use those animations combining with CAAnimationGroup.</p><pre><br>let scaleAnimation = CABasicAnimation(keyPath: “transform.scale.xy”)<br>// some setup<br> <br>let opacityAnimation = CAKeyframeAnimation(keyPath: “opacity”)<br>// some setup<br> <br>animationGroup = CAAnimationGroup()<br>animationGroup.animations = [scaleAnimation, opacityAnimation]</pre><h3>About me</h3><p>I’m an iOS freelancer in Japan, welcome works from abroad.</p><p>You can check my past works here: <a href="https://medium.com/@shu223/my-profile-summary-f14bfc1e7099#.vdh0i7clr">My Profile Summary — Medium</a></p><p>Other contacts:</p><ul><li><a href="https://twitter.com/shu223">Twitter</a></li><li><a href="https://www.facebook.com/shuichi.tsutsumi">Facebook</a></li><li><a href="https://www.linkedin.com/profile/view?id=214896557">LinkedIn</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4de057647936" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Core Bluetooth snippets with Swift]]></title>
            <link>https://medium.com/@shu223/core-bluetooth-snippets-with-swift-9be8524600b2?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/9be8524600b2</guid>
            <category><![CDATA[bluetooth]]></category>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[objective-c]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Wed, 24 Feb 2016 01:40:07 GMT</pubDate>
            <atom:updated>2017-04-18T01:24:11.915Z</atom:updated>
            <content:encoded><![CDATA[<p>This article shows simple examples (code snippets) of Core Bluetooth with Swift &amp; Objective-C.</p><h4>import</h4><p>objc</p><pre>@import CoreBluetooth;</pre><p>swift</p><pre>import CoreBluetooth</pre><h3>Central Manager</h3><h4>Initialize</h4><p><strong>Adopt protocol and define properties.</strong></p><p>objc</p><pre>@interface SomeClass () &lt;CBCentralManagerDelegate&gt;<br>@property (nonatomic, strong) CBCentralManager *centralManager;<br>@property (nonatomic, strong) CBPeripheral *peripheral;<br>@end</pre><p>swift</p><pre>class ComeClass: SomeSuperclass, CBCentralManagerDelegate {<br>    var centralManager: CBCentralManager!<br>    var peripheral: CBPeripheral</pre><p><strong>Initialize CBCentralManager.</strong></p><p>objc</p><pre>self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];</pre><p>swift</p><pre>centralManager = CBCentralManager(delegate: self, queue: nil)</pre><h4>Scan</h4><p><strong>Receive the update of central manager’s state. </strong>**Required**</p><p>objc</p><pre>- (void)centralManagerDidUpdateState:(CBCentralManager *)central {<br>    NSLog(@”state:%ld”, (long)central.state);<br>}</pre><p>swift</p><pre>func centralManagerDidUpdateState(central: CBCentralManager) {<br>    print(“state: \(central.state)”)<br>}</pre><p><strong>Start scanning.</strong></p><p>objc</p><pre>[self.centralManager scanForPeripheralsWithServices:nil options:nil];</pre><p>swift</p><pre>centralManager.scanForPeripheralsWithServices(nil, options: nil)</pre><p><strong>Receive the results of scan.</strong></p><p>objc</p><pre>- (void)   centralManager:(CBCentralManager *)central<br>    didDiscoverPeripheral:(CBPeripheral *)peripheral<br>        advertisementData:(NSDictionary *)advertisementData<br>                     RSSI:(NSNumber *)RSSI<br>{<br>    NSLog(@”peripheral：%@”, peripheral);<br>}</pre><p>swift</p><pre>func centralManager(central: CBCentralManager,<br>    didDiscoverPeripheral peripheral: CBPeripheral,<br>    advertisementData: [String : AnyObject],<br>    RSSI: NSNumber!)<br>{<br>    print(“peripheral: \(peripheral)”)<br>}</pre><p><strong>Stop scanning.</strong></p><p>objc</p><pre>[self.centralManager stopScan];</pre><p>swift</p><pre>centralManager.stopScan()</pre><h4>Connect</h4><p><strong>Start connecting.</strong></p><p>objc</p><pre>[self.centralManager connectPeripheral:peripheral options:nil];</pre><p>swift</p><pre>centralManager.connectPeripheral(peripheral, options: nil)</pre><p><strong>Receive the result of connecting.</strong></p><p>objc</p><pre>// Called when it succeeded<br>- (void)  centralManager:(CBCentralManager *)central<br>    didConnectPeripheral:(CBPeripheral *)peripheral<br>{<br>    NSLog(@”connected!”);<br>}</pre><pre>// Called when it failed<br>- (void)        centralManager:(CBCentralManager *)central<br>    didFailToConnectPeripheral:(CBPeripheral *)peripheral<br>                         error:(NSError *)error<br>{<br>    NSLog(@”failed…”);<br>}</pre><p>swift</p><pre>// Called when it succeeded<br>func centralManager(central: CBCentralManager,<br>    didConnectPeripheral peripheral: CBPeripheral)<br>{<br>    print(“connected!”)<br>}</pre><pre>// Called when it failed<br>func centralManager(central: CBCentralManager,<br>    didFailToConnectPeripheral peripheral: CBPeripheral,<br>    error: NSError?)<br>{<br>    print(“failed…”)<br>}</pre><h4>Discover services &amp; characteristics</h4><p><strong>Adopt CBPeripheralDelegate protocol.</strong></p><p>objc</p><pre>@interface SomeClass () &lt;CBCentralManagerDelegate, CBPeripheralDelegate&gt;</pre><p>swift</p><pre>class SomeClass: SomeSuperclass, CBCentralManagerDelegate, CBPeripheralDelegate {</pre><p><strong>Start discovering services.</strong></p><p>objc</p><pre>peripheral.delegate = self;<br>[peripheral discoverServices:nil];</pre><p>swift</p><pre>peripheral.delegate = self<br>peripheral.discoverServices(nil)</pre><p><strong>Receive the result of discovering services.</strong></p><p>objc</p><pre>- (void)     peripheral:(CBPeripheral *)peripheral<br>    didDiscoverServices:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@”error: %@”, error);<br>        return;<br>    }<br> <br>    NSArray *services = peripheral.services;<br>    NSLog(@”Found %lu services! :%@”, (unsigned long)services.count, services);<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?)<br>{<br>    if let error = error {<br>        print(“error: \(error)”)<br>        return<br>    }</pre><pre>    let services = peripheral.services<br>    print(“Found \(services.count) services! :\(services)”)<br>}</pre><p><strong>Start discovering characteristics.</strong></p><p>objc</p><pre>[peripheral discoverCharacteristics:nil forService:service];</pre><p>swift</p><pre>peripheral.discoverCharacteristics(nil, forService: service)</pre><p><strong>Receive the result of discovering characteristics.</strong></p><p>objc</p><pre>- (void)                      peripheral:(CBPeripheral *)peripheral<br>    didDiscoverCharacteristicsForService:(CBService *)service<br>                                   error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@”error: %@”, error);<br>        return;<br>    }</pre><pre>    NSArray *characteristics = service.characteristics;<br>    NSLog(@”Found %lu characteristics!”, (unsigned long)characteristics.count);<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral,<br>    didDiscoverCharacteristicsForService service: CBService,<br>    error: NSError?)<br>{<br>    if let error = error {<br>        print(“error: \(error)”)<br>        return<br>    }<br><br>    let characteristics = service.characteristics<br>    print(“Found \(characteristics.count) characteristics!)<br>}</pre><h4>Read</h4><p><strong>Start reading the value of a characteristic.</strong></p><p>objc</p><pre>[peripheral readValueForCharacteristic:characteristic];</pre><p>swift</p><pre>peripheral.readValueForCharacteristic(characteristic)</pre><p><strong>Receive the result of reading.</strong></p><p>objc</p><pre>- (void)                 peripheral:(CBPeripheral *)peripheral<br>    didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic<br>                              error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@”Failed… error: %@”, error);<br>        return;<br>    }<br><br>    NSLog(@&quot;characteristic uuid:%@, value%@”,<br>      characteristic.UUID, characteristic.value);<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral,<br>    didUpdateValueForCharacteristic characteristic: CBCharacteristic,<br>    error: NSError?)<br>{<br>    if let error = error {<br>        print(“Failed… error: \(error)”)<br>        return<br>    }<br><br>    print(“characteristic uuid: \(characteristic.UUID), value: \(characteristic.value)”)<br>}</pre><h4>Write</h4><p><strong>Start writing the value of a characteristic.</strong></p><p>objc</p><pre>[peripheral writeValue:data<br>     forCharacteristic:characteristic<br>                  type:CBCharacteristicWriteWithResponse];</pre><p>swift</p><pre>peripheral.writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithResponse)</pre><p><strong>Receive the result of writing.</strong></p><p>objc</p><pre>- (void)                peripheral:(CBPeripheral *)peripheral<br>    didWriteValueForCharacteristic:(CBCharacteristic *)characteristic<br>                             error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@&quot;error:%@”, error);<br>        return;<br>    }<br><br>    NSLog(@”Succeeded!”);<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral,<br>    didWriteValueForCharacteristic characteristic: CBCharacteristic,<br>    error: NSError?)<br>{<br>    if let error = error {<br>        print(“error: \(error)”)<br>        return<br>    }<br><br>    print(“Succeeded!”)<br>}</pre><h4>Notify</h4><p><strong>Start receiving notifications for changes of a characteristic’s value.</strong></p><p>objc</p><pre>[peripheral setNotifyValue:YES<br>         forCharacteristic:characteristic];</pre><p>swift</p><pre>peripheral.setNotifyValue(true, forCharacteristic: characteristic)</pre><p><strong>Stop receiving notifications for changes of a characteristic’s value.</strong></p><p>objc</p><pre>[peripheral setNotifyValue:NO<br>         forCharacteristic:characteristic];</pre><p>swift</p><pre>peripheral.setNotifyValue(false, forCharacteristic: characteristic)</pre><p><strong>Receive the result of starting/stopping to receive notification.</strong></p><p>objc</p><pre>- (void)                             peripheral:(CBPeripheral *)peripheral<br>    didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic<br>                                          error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@&quot;error:%@”, error);<br>    }<br>    else {<br>        NSLog(@&quot;isNotifying:%d”, characteristic.isNotifying);<br>    }<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral,<br>    didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic,<br>    error: NSError?)<br>{<br>    if let error = error { <br>        print(“error: \(error)”)<br>    }<br>    else {<br>        print(“isNotifying: \(characteristic.isNotifying)”)<br>    }<br>}</pre><p><strong>Receive notifications for changes of a characteristic’s value.</strong></p><p>objc</p><pre>- (void) peripheral:(CBPeripheral *)peripheral<br> didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic<br> error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@&quot;error:%@”, error);<br>        return;<br>    }<br><br>    NSLog(@&quot;characteristic UUID:%@, value:%@&quot;,<br>          characteristic.UUID, characteristic.value);<br>}</pre><p>swift</p><pre>func peripheral(peripheral: CBPeripheral,<br> didUpdateValueForCharacteristic characteristic: CBCharacteristic,<br> error: NSError?)<br>{<br>    if let error = error {<br>        print(“error: \(error)”)<br>        return<br>    }<br><br>    print(“characteristic UUID: \(characteristic.UUID), value: \(characteristic.value)”)<br>}</pre><h3>Peripheral Manager</h3><h4>Initialize</h4><p><strong>Adopt protocol and define properties.</strong></p><p>objc</p><pre>@interface SomeClass () &lt;CBPeripheralManagerDelegate&gt;<br>@property (nonatomic, strong) CBPeripheralManager *peripheralManager;<br>@end</pre><p>swift</p><pre>class SomeClass: SomeSuperclass, CBPeripheralManagerDelegate {<br> var peripheralManager: CBPeripheralManager!</pre><p><strong>Initialize CBPeripheralManager.</strong></p><p>objc</p><pre>self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];</pre><p>swift</p><pre>peripheralManager = CBPeripheralManager(delegate: self, queue: nil)</pre><h4>Advertise</h4><p><strong>Receive the update of peripheral manager’s state. </strong>**Required**</p><p>objc</p><pre>- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral<br>{<br>    NSLog(@”state:%ld”, (long)peripheral.state);<br>}</pre><p>swift</p><pre>func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager)<br>{<br>    print(“state: \(peripheral.state)”)<br>}</pre><p><strong>Start advertising.</strong></p><p>objc</p><pre>NSDictionary *advertisementData = @{CBAdvertisementDataLocalNameKey: @”Test Device”};<br>[self.peripheralManager startAdvertising:advertisementData];</pre><p>swift</p><pre>let advertisementData = [CBAdvertisementDataLocalNameKey: “Test Device”]<br>peripheralManager.startAdvertising(advertisementData)</pre><p><strong>Receive the result of starting to advertise.</strong></p><p>objc</p><pre>- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral<br>                                       error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@”Failed… error:%@”, error);<br>        return;<br>    }<br><br>    NSLog(@”Succeeded!”);<br>}</pre><p>swift</p><pre>func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager, error: NSError?)<br>{ <br>    if let error = error {<br>        print(“Failed… error: \(error)”)<br>        return<br>    }</pre><pre>    print(“Succeeded!”)<br>}</pre><p><strong>Stop advertising.</strong></p><p>objc</p><pre>[self.peripheralManager stopAdvertising];</pre><p>swift</p><pre>peripheralManager.stopAdvertising()</pre><h4>Add services</h4><p><strong>Create a service.</strong></p><p>objc</p><pre>CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];<br>CBMutableService *service;<br>service = [[CBMutableService alloc] initWithType:serviceUUID<br>                                         primary:YES];</pre><p>swift</p><pre>let serviceUUID = CBUUID(string: kServiceUUID)<br>let service = CBMutableService(type: serviceUUID, primary: true)</pre><p><strong>Create characteristics.</strong></p><p>objc</p><pre>CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];</pre><pre>CBCharacteristicProperties properties = (<br>    CBCharacteristicPropertyNotify |<br>    CBCharacteristicPropertyRead |<br>    CBCharacteristicPropertyWrite<br>);</pre><pre>CBAttributePermissions permissions = (CBAttributePermissionsReadable | CBAttributePermissionsWriteable);</pre><pre>CBMutableCharacteristic *c;<br>c = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID<br>                                       properties:properties<br>                                            value:nil<br>                                      permissions:permissions];</pre><p>swift</p><pre>let characteristicUUID = CBUUID(string: kCharacteristicUUID)<br>let properties: CBCharacteristicProperties = [.Notify, .Read, .Write]<br>let permissions: CBAttributePermissions = [.Readable, .Writeable]<br>let characteristic = CBMutableCharacteristic(<br>    type: characteristicUUID,<br>    properties: properties,<br>    value: nil,<br>    permissions: permissions)</pre><p><strong>Add characteristics to a service.</strong></p><p>objc</p><pre>service.characteristics = @[characteristic1, characteristic2];</pre><p>swift</p><pre>service.characteristics = [characteristic1, characteristic2]</pre><p><strong>Add a service to the peripheral manager.</strong></p><p>objc</p><pre>[self.peripheralManager addService:service];</pre><p>swift</p><pre>peripheralManager.addService(service)</pre><p><strong>Receive the result of adding a service.</strong></p><p>objc</p><pre>- (void)peripheralManager:(CBPeripheralManager *)peripheral<br>            didAddService:(CBService *)service<br>                    error:(NSError *)error<br>{<br>    if (error) {<br>        NSLog(@&quot;error:%@”, error);<br>        return;<br>    }<br><br>    NSLog(@&quot;service:%@”, service);<br>}</pre><p>swift</p><pre>func peripheralManager(peripheral: CBPeripheralManager, didAddService service: CBService, error: NSError?)<br>{<br>    if let error = error {<br>        print(“error: \(error)”)<br>        return<br>    }<br><br>    print(“service: \(service)”)<br>}</pre><h4>Respond to Read requests</h4><p>objc</p><pre>- (void)peripheralManager:(CBPeripheralManager *)peripheral<br>    didReceiveReadRequest:(CBATTRequest *)request<br>{<br>    if ([request.characteristic.UUID isEqual:self.characteristic.UUID])<br>    {<br>        // Set the characteristic&#39;s value to the request<br>        request.value = self.characteristic.value;<br> <br>        // Respond to the request<br>        [self.peripheralManager respondToRequest:request <br>                                      withResult:CBATTErrorSuccess];<br>    }<br>}</pre><p>swift</p><pre>func peripheralManager(peripheral: CBPeripheralManager, didReceiveReadRequest request: CBATTRequest)<br>{<br>    if request.characteristic.UUID.isEqual(characteristic.UUID)<br>    { <br>        // Set the correspondent characteristic&#39;s value<br>        // to the request<br>        request.value = characteristic.value<br> <br>        // Respond to the request<br>        peripheralManager.respondToRequest(<br>            request,<br>            withResult: .Success)<br>    }<br>}</pre><h4>Respond to Write requests</h4><p>objc</p><pre>- (void)  peripheralManager:(CBPeripheralManager *)peripheral<br>    didReceiveWriteRequests:(NSArray *)requests<br>{<br>    for (CBATTRequest *aRequest in requests)<br>    {<br>        if ([aRequest.characteristic.UUID isEqual:self.characteristic.UUID])<br>        {<br>            // Set the request&#39;s value<br>            // to the correspondent characteristic <br>            self.characteristic.value = aRequest.value;<br>        }<br>    }<br><br>    [self.peripheralManager respondToRequest:requests[0]<br>                                  withResult:CBATTErrorSuccess];<br>}</pre><p>swift</p><pre>func peripheralManager(peripheral: CBPeripheralManager, didReceiveWriteRequests requests: [CBATTRequest])<br>{<br>    for request in requests<br>    {<br>        if request.characteristic.UUID.isEqual(characteristic.UUID) <br>        {<br>            // Set the request&#39;s value<br>            // to the correspondent characteristic<br>            characteristic.value = request.value<br>        }<br>    }</pre><pre>    peripheralManager.respondToRequest(requests[0], withResult: .Success)<br>}</pre><h4>Respond to Notifications/Indications</h4><p><strong>Receive subscribe requests.</strong></p><p>objc</p><pre>- (void)       peripheralManager:(CBPeripheralManager *)peripheral<br>                         central:(CBCentral *)central<br>    didSubscribeToCharacteristic:(CBCharacteristic *)characteristic<br>{<br>    NSLog(@&quot;subscribed centrals: %@”, self.characteristic.subscribedCentrals);<br>}</pre><p>swift</p><pre>func peripheralManager(<br>    peripheral: CBPeripheralManager,<br>    central: CBCentral,<br>    didSubscribeToCharacteristic characteristic: CBCharacteristic)<br>{<br>    print(“subscribed centrals: \( characteristic.subscribedCentrals)”)<br>}</pre><p><strong>Receive unsubscribe requests.</strong></p><p>objc</p><pre>- (void)         peripheralManager:(CBPeripheralManager *)peripheral<br>                           central:(CBCentral *)central<br>  didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic<br>{<br>    NSLog(@&quot;subscribed centrals: %@”, <br>          self.characteristic.subscribedCentrals);<br>}</pre><p>swift</p><pre>func peripheralManager(<br>    peripheral: CBPeripheralManager,<br>    central: CBCentral,<br>    didUnsubscribeFromCharacteristic characteristic: CBCharacteristic)<br>{<br>    print(“subscribed centrals: \(characteristic.subscribedCentrals)”)<br>}</pre><h4>Notify/Indicate updating a value</h4><p>objc</p><pre>self.characteristic.value = data;</pre><pre>[self.peripheralManager updateValue:data<br>                  forCharacteristic:self.characteristic<br>               onSubscribedCentrals:nil];</pre><p>swift</p><pre>characteristic.value = data</pre><pre>peripheralManager.updateValue(<br>    data,<br>    forCharacteristic: characteristic,<br>    onSubscribedCentrals: nil)</pre><h3>About me</h3><p>iOS Freelancer in Tokyo, Japan / Berlin, Germany. <strong>Works from abroad</strong> are welcome.</p><p><a href="https://medium.com/@shu223/my-profile-summary-f14bfc1e7099#.c4aofaj7h"><strong>My Profile Summary</strong></a> (Including contact information)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9be8524600b2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Profile Summary]]></title>
            <link>https://medium.com/@shu223/my-profile-summary-f14bfc1e7099?source=rss-91f89751713------2</link>
            <guid isPermaLink="false">https://medium.com/p/f14bfc1e7099</guid>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[bluetooth]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[freelancing]]></category>
            <dc:creator><![CDATA[Tsutsumi Shuichi]]></dc:creator>
            <pubDate>Sun, 21 Feb 2016 07:51:29 GMT</pubDate>
            <atom:updated>2018-09-24T20:44:14.664Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>Freelance iOS Programmer</strong></p><h3><strong>WORKS</strong></h3><p><strong>Developed over 40 apps from scratch</strong> including many IoT / Wearable related apps using Bluetooth Low Energy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/740/1*NuulwCWvu1RWZ0C1Eb1Wvg.jpeg" /><figcaption><a href="http://whill.us/">Personal Mobility “WHILL”</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/563/1*AYJD0-9Cffp6-PrZzyGKyA.png" /><figcaption><a href="http://www.moff.mobi/">Wearable Toy “Moff Band”</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*icmJUu0OE8zyysdnfQwkZg.jpeg" /><figcaption><a href="https://bonx.co/">Wearable Walkie-Talkie “BONX”</a></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8C4uHg8SDAgOvf4dteAqHw.jpeg" /><figcaption><a href="http://www.rhizomatiks.com/archive/music_for_the_deaf/">Music for the Deaf</a></figcaption></figure><h3><strong>Books</strong></h3><p><strong>The Author of 2 books</strong></p><ul><li><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/">iOS x BLE Core Bluetooth Programming</a>”​ (2015)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*NApBPzKR_GK0hMyV4l0XJw.jpeg" /><figcaption><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4883379736/22301-22/ref=nosim/">amazon.co.jp</a></figcaption></figure><ul><li>“iOS Programming — Advanced 100 Recipes” (2013)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*Jf9SmZ5rZQsNizEmKqcymA.jpeg" /><figcaption><a href="http://www.amazon.co.jp/dp/4798038180">amazon.co.jp</a></figcaption></figure><h3><strong>Open Sources</strong></h3><p><strong>The owner of famous open source repositories</strong></p><ul><li><a href="https://github.com/shu223/iOS-10-Sampler">iOS-10-Sampler</a> #1 on GitHub trending, over 3000 stars (2016)</li><li><a href="https://github.com/shu223/iOS-9-Sampler">iOS-9-Sampler</a> #1 on GitHub trending, over 3000 stars (2015)</li><li>#1 on GitHub trending, over 3000 stars (2015)</li><li><a href="https://github.com/shu223/iOS8-Sampler">iOS-8-Sample</a>r #1 on GitHub trending, over 3000 stars (2014)</li><li><a href="https://github.com/shu223/iOS7-Sampler">iOS-7-Sampler</a> #1 on GitHub trending, over 3000 stars (2013)</li><li><a href="https://github.com/shu223/AnimatedTransitionGallery">AnimatedTransitionGallery</a> #1 on GitHub trending, over 1500 stars (2014)</li><li><a href="https://github.com/shu223/PulsingHalo">PulsingHalo</a> over 1000 stars (2013)</li><li><a href="https://github.com/shu223/watchOS-2-Sampler">watchOS-2-Sampler</a> nearly 1000 stars (2015)</li></ul><p><a href="https://github.com/shu223">and so on</a>.</p><p><a href="https://gitstar-ranking.com/shu223">TOTAL <strong>20,000 </strong>stars</a> / <a href="http://github-awards.com/users/shu223"><strong>#7 </strong>on<strong> </strong>GitHub Awards</a> <strong>WORLDWIDE</strong> rank in ObjC</p><h3>Presentations</h3><ul><li><a href="https://realm.io/news/altconf-shuichi-tsutsumi-practical-core-bluetooth/">AltConf 2016</a> (San Francisco)</li><li>UIConf 2016 (Berlin)</li><li>iOSCon 2016 (London)</li></ul><h3>Contacts</h3><ul><li><a href="https://linkedin.com/in/shuichi-tsutsumi-525b755b">LinkedIn</a></li><li><a href="https://github.com/shu223">GitHub</a></li><li><a href="/service/mailto://shuichi0526@gmail.com">Email</a></li><li><a href="https://twitter.com/shu223">Twitter</a></li><li><a href="https://www.facebook.com/shuichi.tsutsumi">Facebook</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f14bfc1e7099" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>