mp Ampersand, the Aftermath By feedproxy.google.com Published On :: Wed, 22 Jun 2011 12:28:38 PDT The first Ampersand web typography conference took place in Brighton last Friday. Ampersand was ace. I’m going to say that again with emphasis: Ampersand was ace! Like the Ready Brek kid from the 80s TV ads I’m glowing with good vibes. Imagine you’d just met some of the musicians that created the soundtrack to your life. That’s pretty much how I feel. Nerves and all… Photo by Ben Mitchell. For a long, long time I’ve gazed across at the typography community with something akin to awe at the work they do. I’ve lurked quietly on the ATypI mailing list, in the Typophile forum, and behind the glass dividing my eyes from the blogs, portfolios, and galleries. I always had a sneaking suspicion the web and type design communities had much in common: Excellence born from actual client work; techniques and skills refined by practice, not in a lab or classroom; a willingness to share and disseminate, most clearly demonstrated at Typophile and through web designer’s own blogs. The people of both professions have a very diverse set of backgrounds from graphic design all the way through to engineering, to accidentally working in a print shop. We’ve been apprenticed to our work, and Ampersand was a celebration of what we’ve achieved so far and what’s yet to come. Of course, web design is a new profession. Type design has a history that spans hundreds of years. Nevertheless, both professions are self-actualising. Few courses exist of any real merit. There is no qualifications authority. The work from both arenas succeeds or fails based on whether it works or not. Ampersand was the first event of its kind. Folks from both communities came together around the mutal fascination, frustration, challenge and opportunity of web type. Like Brooklyn Beta, the audience was as fantastic as the line up. I met folks like Yves Peters of the FontFeed, Mike Duggan of Microsoft Typography, Jason Smith, Phil Garnham, Fernando Mello, and Emanuela Conidi of Fontsmith, Veronica Burian of TypeTogether, Adam Twardoch of Fontlab and MyFonts, Nick Sherman of of Webtype, Mandy Brown of A Book Apart and Typekit, and many, many others. (Sorry for stopping there, but wow, it would be a huge list.) Rich Rutter Rich Rutter opened the day on behalf of Clearleft and Fontdeck at the Brighton Dome. Rich and I had talked about a web typography conference before. He just went out and did it. Hats off to him, and people like Sophie Barrett at Clearleft who helped make the day run so smoothly. Others have written comprehensive, insightful summaries of the day and the talks. Much better than I could, sitting there on the day, rapt, taking no notes. What follows are a few snippets my memory threw out when prodded. Vincent Connare Who knew the original letterforms for Comic Sans were inspired by a copy of The Watchmen Vincent Connare had in his office? Or that Vincent, who also designed Trebuchet, considers himself an engineer rather than type designer, and is working at the moment on the Ubuntu fonts with colleagues at Dalton Maag. Jason Santa Maria declared himself a type nerd, and gave a supremely detailed talk about selecting, setting, and understanding web type. Wonderful stuff. Jason Santa Maria Jonathan Hoefler talked in rapid, articulate, and precise terms about the work behind upcoming release of pretty-much all of H&FJ’s typefaces as web fonts. (Hooray!) He clearly and wonderfully explained how they took the idea behind their typefaces, and moved them through a design process to produce a final form for a specific purpose. In this case, the web, as a distinct and different environment from print. Jonathan Hoefler Photo by Sean Johnson. I spoke between Jason and Jonathan. Gulp. After staying up until 4am the night before, anxiously working on slides, I was carried along by the privilege and joy of being there, hopefully without too much mumbling or squinting with bleary eyes. After lunch, David Berlow continued the story of web fonts, taking us on a journey through his own trials and tribulations at Font Bureau when re-producing typefaces for the web crude media. His dry, droll, richly-flavoured delivery was a humorous counterpoint to some controversial asides. David Berlow Photo by Jeremy Keith. John Daggett of Mozilla, editor of the CSS3 Fonts Module, talked with great empathy for web designers about the amazing typographic advances we’re about to see in browsers. Tim Brown of Typekit followed. Tim calmly and thoroughly advocated the extension of modular scales to all aspects of a web interface, taking values from the body type and building all elements with those values as the common denominator. Finally, Mark Boulton wrapped up the day brilliantly, describing the designer’s role as the mitigator of entropy, reversing the natural trend for things to move from order to chaos, and a theme he’s exploring at the moment: designing from the content out. Mark Boulton The tone of the day was fun, thoughtful, articulate, and exacting. All the talks were a mix of anecdotal and observational humour, type nerdery, and most of all an overwhelming commitment to excellence in web typography. It was a journey in itself. Decades of experience from plate and press, screen, and web was being distilled into 45-minute presentations. I loved it. As always, one of the most enjoyable bits for me was the hallway track. I talked to heaps of people both in the pre- and after-party, and in between the talks on the day itself. I heard stories, ideas, and opinions from print designers, web designers, type designers, font developers, and writers. We talked late into the night. We talked more the next day. Now the talking has paused for a while, my thoughts are manifold. I can honestly say, I’ve never been so filled with positivity about where we are, and where we’re going. Web typography is here, it works, it’s better all the time, and one day web and type designers everywhere will wonder, perplexed, as they try to imagine what the web was like before. Here’s to another Ampersand next year! I’m now going to see if Rich needs any encouragement to do it again. I’m guessing not, but if he does, I aim to provide it, vigorously. I hope I see you there! Furthermore Rich Rutter back in May on The Ampersand Story Eye Magazine: Web typography comes of age at Brighton’s Ampersand conference Anthony Stonehouse: Ampersand 2011 Laura Kalbag: Notes from Ampersand Dave Bushell: Ampersand Conference! Last but not least, did I mention that Rich Rutter, Mark Boulton, and I are writing a book? We are! More on that another time, but until then, follow @webtypography for intermittent updates. Full Article
mp New Auphonic Privacy Policy and GDPR Compliance By feedproxy.google.com Published On :: Thu, 24 May 2018 09:26:55 +0000 The new General Data Protection Regulation (GDPR) of the European Union will be implemented on May 25th, 2018. We used this opportunity to rework many of our internal data processing structures, removed unnecessary trackers and apply this strict and transparent regulation also to all our customers worldwide. Image from pixapay.com. At Auphonic we store as few personal information as possible about your usage and production data. Here are a few human-readable excerpts from our privacy policy about which information we collect, how we process it, how long and where we store it - for more details please see our full Privacy Policy. Information that we collect Your email address when you create an account. Your files, content, configuration parameters and other information, including your photos, audio or video files, production settings, metadata and emails. Your tokens or authentication information if you choose to connect to any External services. Your subscription plan, credits purchases and production billing history associated with your account, where applicable. Your interactions with us, whether by email, on our blog or on our social media platforms. We do not process any special categories of data (also commonly referred to as “sensitive personal data”). How we use and process your Data To authenticate you when you log on to your account. To run your Productions, such that Auphonic can create new media files from your Content according to your instructions. To improve our audio processing algorithms. For this purpose, you agree that your Content may be viewed and/or listened to by an Auphonic employee or any person contracted by Auphonic to work on our audio processing algorithms. To connect your Auphonic account to an External service according to your instructions. To develop, improve and optimize the contents, screen layouts and features of our Services. To follow up on any question and request for assistance or information. When using our Service, you fully retain any rights that you have with regards to your Content, including copyright. How long we store your Information Your Productions and any associated audio or video files will be permanently deleted from our servers including all its metadata and possible data from external services after 21 days (7 days for video productions). We will, however, keep billing metadata associated with your Productions in an internal database (how many hours of audio you processed). Also, we might store selected audio and/or video files (or excerpts thereof) from your Content in an internal storage space for the purpose of improving our audio processing algorithms. Other information like Presets, connected External services, Account settings etc. will be stored until you delete them or when your account is deleted. Where we store your Data All data that we collect from you is stored on secure servers in the European Economic Area (in Germany). More Information and Contact For more information please read our full Privacy Policy. Please do not hesitate to contact us regarding any matter relating to our privacy policy and GDPR compliance! Full Article News
mp New Auphonic Transcript Editor and Improved Speech Recognition Services By feedproxy.google.com Published On :: Tue, 03 Jul 2018 05:35:25 +0000 Back in late 2016, we introduced Speech Recognition at Auphonic. This allows our users to create transcripts of their recordings, and more usefully, this means podcasts become searchable. Now we integrated two more speech recognition engines: Amazon Transcribe and Speechmatics. Whilst integrating these services, we also took the opportunity to develop a complete new Transcription Editor: Screenshot of our Transcript Editor with word confidence highlighting and the edit bar. Try out the Transcript Editor Examples yourself! The new Auphonic Transcript Editor is included directly in our HTML transcript output file, displays word confidence values to instantly see which sections should be checked manually, supports direct audio playback, HTML/PDF/WebVTT export and allows you to share the editor with someone else for further editing. The new services, Amazon Transcribe and Speechmatics, offer transcription quality improvements compared to our other integrated speech recognition services. They also return word confidence values, timestamps and some punctuation, which is exported to our output files. The Auphonic Transcript Editor With the integration of the two new services offering improved recognition quality and word timestamps alongside confidence scores, we realized that we could leverage these improvements to give our users easy-to-use transcription editing. Therefore we developed a new, open source transcript editor, which is embedded directly in our HTML output file and has been designed to make checking and editing transcripts as easy as possible. Main features of our transcript editor: Edit the transcription directly in the HTML document. Show/hide word confidence, to instantly see which sections should be checked manually (if you use Amazon Transcribe or Speechmatics as speech recognition engine). Listen to audio playback of specific words directly in the HTML editor. Share the transcript editor with others: as the editor is embedded directly in the HTML file (no external dependencies), you can just send the HTML file to some else to manually check the automatically generated transcription. Export the edited transcript to HTML, PDF or WebVTT. Completely useable on all mobile devices and desktop browsers. Examples: Try Out the Transcript Editor Here are two examples of the new transcript editor, taken from our speech recognition audio examples page: 1. Singletrack Transcript Editor Example Singletrack speech recognition example from the first 10 minutes of Common Sense 309 by Dan Carlin. Speechmatics was used as speech recognition engine without any keywords or further manual editing. 2. Multitrack Transcript Editor Example A multitrack automatic speech recognition transcript example from the first 20 minutes of TV Eye on Marvel - Luke Cage S1E1. Amazon Transcribe was used as speech recognition engine without any further manual editing. As this is a multitrack production, the transcript includes exact speaker names as well (try to edit them!). Transcript Editing By clicking the Edit Transcript button, a dashed box appears around the text. This indicates that the text is now freely editable on this page. Your changes can be saved by using one of the export options (see below). If you make a mistake whilst editing, you can simply use the undo/redo function of the browser to undo or redo your changes. When working with multitrack productions, another helpful feature is the ability to change all speaker names at once throughout the whole transcript just by editing one speaker. Simply click on an instance of a speaker title and change it to the appropriate name, this name will then appear throughout the whole transcript. Word Confidence Highlighting Word confidence values are shown visually in the transcript editor, highlighted in shades of red (see screenshot above). The shade of red is dependent on the actual word confidence value: The darker the red, the lower the confidence value. This means you can instantly see which sections you should check/re-work manually to increase the accuracy. Once you have edited the highlighted text, it will be set to white again, so it’s easy to see which sections still require editing. Use the button Add/Remove Highlighting to disable/enable word confidence highlighting. NOTE: Word confidence values are only available in Amazon Transcribe or Speechmatics, not if you use our other integrated speech recognition services! Audio Playback The button Activate/Stop Play-on-click allows you to hear the audio playback of the section you click on (by clicking directly on the word in the transcript editor). This is helpful in allowing you to check the accuracy of certain words by being able to listen to them directly whilst editing, without having to go back and try to find that section within your audio file. If you use an External Service in your production to export the resulting audio file, we will automatically use the exported file in the transcript editor. Otherwise we will use the output file generated by Auphonic. Please note that this file is password protected for the current Auphonic user and will be deleted in 21 days. If no audio file is available in the transcript editor, or cannot be played because of the password protection, you will see the button Add Audio File to add a new audio file for playback. Export Formats, Save/Share Transcript Editor Click on the button Export... to see all export and saving/sharing options: Save/Share Editor The Save Editor button stores the whole transcript editor with all its current changes into a new HTML file. Use this button to save your changes for further editing or if you want to share your transcript with someone else for manual corrections (as the editor is embedded directly in the HTML file without any external dependencies). Export HTML / Export PDF / Export WebVTT Use one of these buttons to export the edited transcript to HTML (for WordPress, Word, etc.), to PDF (via the browser print function) or to WebVTT (so that the edited transcript can be used as subtitles or imported in web audio players of the Podlove Publisher or Podigee). Every export format is rendered directly in the browser, no server needed. Amazon Transcribe The first of the two new services, Amazon Transcribe, offers accurate transcriptions in English and Spanish at low costs, including keywords, word confidence, timestamps, and punctuation. UPDATE 2019: Amazon Transcribe offers more languages now - please see Amazon Transcribe Features! Pricing The free tier offers 60 minutes of free usage a month for 12 months. After that, it is billed monthly at a rate of $0.0004 per second ($1.44/h). More information is available at Amazon Transcribe Pricing. Custom Vocabulary (Keywords) Support Custom Vocabulary (called Keywords in Auphonic) gives you the ability to expand and customize the speech recognition vocabulary, specific to your case (i.e. product names, domain-specific terminology, or names of individuals). The same feature is also available in the Google Cloud Speech API. Timestamps, Word Confidence, and Punctuation Amazon Transcribe returns a timestamp and confidence value for each word so that you can easily locate the audio in the original recording by searching for the text. It also adds some punctuation, which is combined with our own punctuation and formatting automatically. The high-quality (especially in combination with keywords) and low costs of Amazon Transcribe make it attractive, despite only currently supporting two languages. However, the processing time of Amazon Transcribe is much slower compared to all our other integrated services! Try it yourself: Connect your Auphonic account with Amazon Transcribe at our External Services Page. Speechmatics Speechmatics offers accurate transcriptions in many languages including word confidence values, timestamps, and punctuation. Many Languages Speechmatics’ clear advantage is the sheer number of languages it supports (all major European and some Asiatic languages). It also has a Global English feature, which supports different English accents during transcription. Timestamps, Word Confidence, and Punctuation Like Amazon, Speechmatics creates timestamps, word confidence values, and punctuation. Pricing Speechmatics is the most expensive speech recognition service at Auphonic. Pricing starts at £0.06 per minute of audio and can be purchased in blocks of £10 or £100. This equates to a starting rate of about $4.78/h. Reduced rate of £0.05 per minute ($3.98/h) are available if purchasing £1,000 blocks. They offer significant discounts for users requiring higher volumes. At this further reduced price point it is a similar cost to the Google Speech API (or lower). If you process a lot of content, you should contact them directly at sales@speechmatics.com and say that you wish to use it with Auphonic. More information is available at Speechmatics Pricing. Speechmatics offers high-quality transcripts in many languages. But these features do come at a price, it is the most expensive speech recognition services at Auphonic. Unfortunately, their existing Custom Dictionary (keywords) feature, which would further improve the results, is not available in the Speechmatics API yet. Try it yourself: Connect your Auphonic account with Speechmatics at our External Services Page. What do you think? Any feedback about the new speech recognition services, especially about the recognition quality in various languages, is highly appreciated. We would also like to hear any comments you have on the transcript editor particularly - is there anything missing, or anything that could be implemented better? Please let us know! Full Article Audio News
mp Concurrency & Multithreading in iOS By feedproxy.google.com Published On :: Tue, 25 Feb 2020 08:00:00 -0500 Concurrency is the notion of multiple things happening at the same time. This is generally achieved either via time-slicing, or truly in parallel if multiple CPU cores are available to the host operating system. We've all experienced a lack of concurrency, most likely in the form of an app freezing up when running a heavy task. UI freezes don't necessarily occur due to the absence of concurrency — they could just be symptoms of buggy software — but software that doesn't take advantage of all the computational power at its disposal is going to create these freezes whenever it needs to do something resource-intensive. If you've profiled an app hanging in this way, you'll probably see a report that looks like this: Anything related to file I/O, data processing, or networking usually warrants a background task (unless you have a very compelling excuse to halt the entire program). There aren't many reasons that these tasks should block your user from interacting with the rest of your application. Consider how much better the user experience of your app could be if instead, the profiler reported something like this: Analyzing an image, processing a document or a piece of audio, or writing a sizeable chunk of data to disk are examples of tasks that could benefit greatly from being delegated to background threads. Let's dig into how we can enforce such behavior into our iOS applications. A Brief History In the olden days, the maximum amount of work per CPU cycle that a computer could perform was determined by the clock speed. As processor designs became more compact, heat and physical constraints started becoming limiting factors for higher clock speeds. Consequentially, chip manufacturers started adding additional processor cores on each chip in order to increase total performance. By increasing the number of cores, a single chip could execute more CPU instructions per cycle without increasing its speed, size, or thermal output. There's just one problem... How can we take advantage of these extra cores? Multithreading. Multithreading is an implementation handled by the host operating system to allow the creation and usage of n amount of threads. Its main purpose is to provide simultaneous execution of two or more parts of a program to utilize all available CPU time. Multithreading is a powerful technique to have in a programmer's toolbelt, but it comes with its own set of responsibilities. A common misconception is that multithreading requires a multi-core processor, but this isn't the case — single-core CPUs are perfectly capable of working on many threads, but we'll take a look in a bit as to why threading is a problem in the first place. Before we dive in, let's look at the nuances of what concurrency and parallelism mean using a simple diagram: In the first situation presented above, we observe that tasks can run concurrently, but not in parallel. This is similar to having multiple conversations in a chatroom, and interleaving (context-switching) between them, but never truly conversing with two people at the same time. This is what we call concurrency. It is the illusion of multiple things happening at the same time when in reality, they're switching very quickly. Concurrency is about dealing with lots of things at the same time. Contrast this with the parallelism model, in which both tasks run simultaneously. Both execution models exhibit multithreading, which is the involvement of multiple threads working towards one common goal. Multithreading is a generalized technique for introducing a combination of concurrency and parallelism into your program. The Burden of Threads A modern multitasking operating system like iOS has hundreds of programs (or processes) running at any given moment. However, most of these programs are either system daemons or background processes that have very low memory footprint, so what is really needed is a way for individual applications to make use of the extra cores available. An application (process) can have many threads (sub-processes) operating on shared memory. Our goal is to be able to control these threads and use them to our advantage. Historically, introducing concurrency to an app has required the creation of one or more threads. Threads are low-level constructs that need to be managed manually. A quick skim through Apple's Threaded Programming Guide is all it takes to see how much complexity threaded code adds to a codebase. In addition to building an app, the developer has to: Responsibly create new threads, adjusting that number dynamically as system conditions change Manage them carefully, deallocating them from memory once they have finished executing Leverage synchronization mechanisms like mutexes, locks, and semaphores to orchestrate resource access between threads, adding even more overhead to application code Mitigate risks associated with coding an application that assumes most of the costs associated with creating and maintaining any threads it uses, and not the host OS This is unfortunate, as it adds enormous levels of complexity and risk without any guarantees of improved performance. Grand Central Dispatch iOS takes an asynchronous approach to solving the concurrency problem of managing threads. Asynchronous functions are common in most programming environments, and are often used to initiate tasks that might take a long time, like reading a file from the disk, or downloading a file from the web. When invoked, an asynchronous function executes some work behind the scenes to start a background task, but returns immediately, regardless of how long the original task might takes to actually complete. A core technology that iOS provides for starting tasks asynchronously is Grand Central Dispatch (or GCD for short). GCD abstracts away thread management code and moves it down to the system level, exposing a light API to define tasks and execute them on an appropriate dispatch queue. GCD takes care of all thread management and scheduling, providing a holistic approach to task management and execution, while also providing better efficiency than traditional threads. Let's take a look at the main components of GCD: What've we got here? Let's start from the left: DispatchQueue.main: The main thread, or the UI thread, is backed by a single serial queue. All tasks are executed in succession, so it is guaranteed that the order of execution is preserved. It is crucial that you ensure all UI updates are designated to this queue, and that you never run any blocking tasks on it. We want to ensure that the app's run loop (called CFRunLoop) is never blocked in order to maintain the highest framerate. Subsequently, the main queue has the highest priority, and any tasks pushed onto this queue will get executed immediately. DispatchQueue.global: A set of global concurrent queues, each of which manage their own pool of threads. Depending on the priority of your task, you can specify which specific queue to execute your task on, although you should resort to using default most of the time. Because tasks on these queues are executed concurrently, it doesn't guarantee preservation of the order in which tasks were queued. Notice how we're not dealing with individual threads anymore? We're dealing with queues which manage a pool of threads internally, and you will shortly see why queues are a much more sustainable approach to multhreading. Serial Queues: The Main Thread As an exercise, let's look at a snippet of code below, which gets fired when the user presses a button in the app. The expensive compute function can be anything. Let's pretend it is post-processing an image stored on the device. import UIKit class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { compute() } private func compute() -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } At first glance, this may look harmless, but if you run this inside of a real app, the UI will freeze completely until the loop is terminated, which will take... a while. We can prove it by profiling this task in Instruments. You can fire up the Time Profiler module of Instruments by going to Xcode > Open Developer Tool > Instruments in Xcode's menu options. Let's look at the Threads module of the profiler and see where the CPU usage is highest. We can see that the Main Thread is clearly at 100% capacity for almost 5 seconds. That's a non-trivial amount of time to block the UI. Looking at the call tree below the chart, we can see that the Main Thread is at 99.9% capacity for 4.43 seconds! Given that a serial queue works in a FIFO manner, tasks will always complete in the order in which they were inserted. Clearly the compute() method is the culprit here. Can you imagine clicking a button just to have the UI freeze up on you for that long? Background Threads How can we make this better? DispatchQueue.global() to the rescue! This is where background threads come in. Referring to the GCD architecture diagram above, we can see that anything that is not the Main Thread is a background thread in iOS. They can run alongside the Main Thread, leaving it fully unoccupied and ready to handle other UI events like scrolling, responding to user events, animating etc. Let's make a small change to our button click handler above: class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { DispatchQueue.global(qos: .userInitiated).async { [unowned self] in self.compute() } } private func compute() -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } Unless specified, a snippet of code will usually default to execute on the Main Queue, so in order to force it to execute on a different thread, we'll wrap our compute call inside of an asynchronous closure that gets submitted to the DispatchQueue.global queue. Keep in mind that we aren't really managing threads here. We're submitting tasks (in the form of closures or blocks) to the desired queue with the assumption that it is guaranteed to execute at some point in time. The queue decides which thread to allocate the task to, and it does all the hard work of assessing system requirements and managing the actual threads. This is the magic of Grand Central Dispatch. As the old adage goes, you can't improve what you can't measure. So we measured our truly terrible button click handler, and now that we've improved it, we'll measure it once again to get some concrete data with regards to performance. Looking at the profiler again, it's quite clear to us that this is a huge improvement. The task takes an identical amount of time, but this time, it's happening in the background without locking up the UI. Even though our app is doing the same amount of work, the perceived performance is much better because the user will be free to do other things while the app is processing. You may have noticed that we accessed a global queue of .userInitiated priority. This is an attribute we can use to give our tasks a sense of urgency. If we run the same task on a global queue of and pass it a qos attribute of background , iOS will think it's a utility task, and thus allocate fewer resources to execute it. So, while we don't have control over when our tasks get executed, we do have control over their priority. A Note on Main Thread vs. Main Queue You might be wondering why the Profiler shows "Main Thread" and why we're referring to it as the "Main Queue". If you refer back to the GCD architecture we described above, the Main Queue is solely responsible for managing the Main Thread. The Dispatch Queues section in the Concurrency Programming Guide says that "the main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application." The terms "execute on the Main Thread" and "execute on the Main Queue" can be used interchangeably. Concurrent Queues So far, our tasks have been executed exclusively in a serial manner. DispatchQueue.main is by default a serial queue, and DispatchQueue.global gives you four concurrent dispatch queues depending on the priority parameter you pass in. Let's say we want to take five images, and have our app process them all in parallel on background threads. How would we go about doing that? We can spin up a custom concurrent queue with an identifier of our choosing, and allocate those tasks there. All that's required is the .concurrent attribute during the construction of the queue. class ViewController: UIViewController { let queue = DispatchQueue(label: "com.app.concurrentQueue", attributes: .concurrent) let images: [UIImage] = [UIImage].init(repeating: UIImage(), count: 5) @IBAction func handleTap(_ sender: Any) { for img in images { queue.async { [unowned self] in self.compute(img) } } } private func compute(_ img: UIImage) -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } Running that through the profiler, we can see that the app is now spinning up 5 discrete threads to parallelize a for-loop. Parallelization of N Tasks So far, we've looked at pushing computationally expensive task(s) onto background threads without clogging up the UI thread. But what about executing parallel tasks with some restrictions? How can Spotify download multiple songs in parallel, while limiting the maximum number up to 3? We can go about this in a few ways, but this is a good time to explore another important construct in multithreaded programming: semaphores. Semaphores are signaling mechanisms. They are commonly used to control access to a shared resource. Imagine a scenario where a thread can lock access to a certain section of the code while it executes it, and unlocks after it's done to let other threads execute the said section of the code. You would see this type of behavior in database writes and reads, for example. What if you want only one thread writing to a database and preventing any reads during that time? This is a common concern in thread-safety called Readers-writer lock. Semaphores can be used to control concurrency in our app by allowing us to lock n number of threads. let kMaxConcurrent = 3 // Or 1 if you want strictly ordered downloads! let semaphore = DispatchSemaphore(value: kMaxConcurrent) let downloadQueue = DispatchQueue(label: "com.app.downloadQueue", attributes: .concurrent) class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { for i in 0..<15 { downloadQueue.async { [unowned self] in // Lock shared resource access semaphore.wait() // Expensive task self.download(i + 1) // Update the UI on the main thread, always! DispatchQueue.main.async { tableView.reloadData() // Release the lock semaphore.signal() } } } } func download(_ songId: Int) -> Void { var counter = 0 // Simulate semi-random download times. for _ in 0..<Int.random(in: 999999...10000000) { counter += songId } } } Notice how we've effectively restricted our download system to limit itself to k number of downloads. The moment one download finishes (or thread is done executing), it decrements the semaphore, allowing the managing queue to spawn another thread and start downloading another song. You can apply a similar pattern to database transactions when dealing with concurrent reads and writes. Semaphores usually aren't necessary for code like the one in our example, but they become more powerful when you need to enforce synchronous behavior whille consuming an asynchronous API. The above could would work just as well with a custom NSOperationQueue with a maxConcurrentOperationCount, but it's a worthwhile tangent regardless. Finer Control with OperationQueue GCD is great when you want to dispatch one-off tasks or closures into a queue in a 'set-it-and-forget-it' fashion, and it provides a very lightweight way of doing so. But what if we want to create a repeatable, structured, long-running task that produces associated state or data? And what if we want to model this chain of operations such that they can be cancelled, suspended and tracked, while still working with a closure-friendly API? Imagine an operation like this: This would be quite cumbersome to achieve with GCD. We want a more modular way of defining a group of tasks while maintaining readability and also exposing a greater amount of control. In this case, we can use Operation objects and queue them onto an OperationQueue, which is a high-level wrapper around DispatchQueue. Let's look at some of the benefits of using these abstractions and what they offer in comparison to the lower-level GCI API: You may want to create dependencies between tasks, and while you could do this via GCD, you're better off defining them concretely as Operation objects, or units of work, and pushing them onto your own queue. This would allow for maximum reusability since you may use the same pattern elsewhere in an application. The Operation and OperationQueue classes have a number of properties that can be observed, using KVO (Key Value Observing). This is another important benefit if you want to monitor the state of an operation or operation queue. Operations can be paused, resumed, and cancelled. Once you dispatch a task using Grand Central Dispatch, you no longer have control or insight into the execution of that task. The Operation API is more flexible in that respect, giving the developer control over the operation's life cycle. OperationQueue allows you to specify the maximum number of queued operations that can run simultaneously, giving you a finer degree of control over the concurrency aspects. The usage of Operation and OperationQueue could fill an entire blog post, but let's look at a quick example of what modeling dependencies looks like. (GCD can also create dependencies, but you're better off dividing up large tasks into a series of composable sub-tasks.) In order to create a chain of operations that depend on one another, we could do something like this: class ViewController: UIViewController { var queue = OperationQueue() var rawImage = UIImage? = nil let imageUrl = URL(string: "https://example.com/portrait.jpg")! @IBOutlet weak var imageView: UIImageView! let downloadOperation = BlockOperation { let image = Downloader.downloadImageWithURL(url: imageUrl) OperationQueue.main.async { self.rawImage = image } } let filterOperation = BlockOperation { let filteredImage = ImgProcessor.addGaussianBlur(self.rawImage) OperationQueue.main.async { self.imageView = filteredImage } } filterOperation.addDependency(downloadOperation) [downloadOperation, filterOperation].forEach { queue.addOperation($0) } } So why not opt for a higher level abstraction and avoid using GCD entirely? While GCD is ideal for inline asynchronous processing, Operation provides a more comprehensive, object-oriented model of computation for encapsulating all of the data around structured, repeatable tasks in an application. Developers should use the highest level of abstraction possible for any given problem, and for scheduling consistent, repeated work, that abstraction is Operation. Other times, it makes more sense to sprinkle in some GCD for one-off tasks or closures that we want to fire. We can mix both OperationQueue and GCD to get the best of both worlds. The Cost of Concurrency DispatchQueue and friends are meant to make it easier for the application developer to execute code concurrently. However, these technologies do not guarantee improvements to the efficiency or responsiveness in an application. It is up to you to use queues in a manner that is both effective and does not impose an undue burden on other resources. For example, it's totally viable to create 10,000 tasks and submit them to a queue, but doing so would allocate a nontrivial amount of memory and introduce a lot of overhead for the allocation and deallocation of operation blocks. This is the opposite of what you want! It's best to profile your app thoroughly to ensure that concurrency is enhancing your app's performance and not degrading it. We've talked about how concurrency comes at a cost in terms of complexity and allocation of system resources, but introducing concurrency also brings a host of other risks like: Deadlock: A situation where a thread locks a critical portion of the code and can halt the application's run loop entirely. In the context of GCD, you should be very careful when using the dispatchQueue.sync { } calls as you could easily get yourself in situations where two synchronous operations can get stuck waiting for each other. Priority Inversion: A condition where a lower priority task blocks a high priority task from executing, which effectively inverts their priorities. GCD allows for different levels of priority on its background queues, so this is quite easily a possibility. Producer-Consumer Problem: A race condition where one thread is creating a data resource while another thread is accessing it. This is a synchronization problem, and can be solved using locks, semaphores, serial queues, or a barrier dispatch if you're using concurrent queues in GCD. ...and many other sorts of locking and data-race conditions that are hard to debug! Thread safety is of the utmost concern when dealing with concurrency. Parting Thoughts + Further Reading If you've made it this far, I applaud you. Hopefully this article gives you a lay of the land when it comes to multithreading techniques on iOS, and how you can use some of them in your app. We didn't get to cover many of the lower-level constructs like locks, mutexes and how they help us achieve synchronization, nor did we get to dive into concrete examples of how concurrency can hurt your app. We'll save those for another day, but you can dig into some additional reading and videos if you're eager to dive deeper. Building Concurrent User Interfaces on iOS (WWDC 2012) Concurrency and Parallelism: Understanding I/O Apple's Official Concurrency Programming Guide Mutexes and Closure Capture in Swift Locks, Thread Safety, and Swift Advanced NSOperations (WWDC 2015) NSHipster: NSOperation Full Article Code
mp CLI Equivalents for Common MAMP PRO and Sequel Pro Tasks By feedproxy.google.com Published On :: Thu, 26 Mar 2020 00:00:00 -0400 Working on website front ends I sometimes use MAMP PRO to manage local hosts and Sequel Pro to manage databases. Living primarily in my text editor, a terminal, and a browser window, moving to these click-heavy dedicated apps can feel clunky. Happily, the tasks I have most frequently turned to those apps for —starting and stopping servers, creating new hosts, and importing, exporting, deleting, and creating databases— can be done from the command line. I still pull up MAMP PRO if I need to change a host's PHP version or work with its other more specialized settings, or Sequel Pro to quickly inspect a database, but for the most part I can stay on the keyboard and in my terminal. Here's how: Command Line MAMP PRO You can start and stop MAMP PRO's servers from the command line. You can even do this when the MAMP PRO desktop app isn't open. Note: MAMP PRO's menu icon will not change color to reflect the running/stopped status when the status is changed via the command line. Start the MAMP PRO servers: /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd startServers Stop the MAMP PRO servers: /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd stopServers Create a host (replace host_name and root_path): /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd createHost host_name root_path MAMP PRO-friendly Command Line Sequel Pro Note: if you don't use MAMP PRO, just replace the /Applications/MAMP/Library/bin/mysql with mysql. In all of the following commands, replace username with your user name (locally this is likely root) and database_name with your database name. The -p (password) flag with no argument will trigger an interactive password prompt. This is more secure than including your password in the command itself (like -pYourPasswordHere). Of course, if you're using the default password root is not particular secure to begin with so you might just do -pYourPasswordHere. Setting the -h (host) flag to localhost or 127.0.0.1 tells mysql to look at what's on localhost. With the MAMP PRO servers running, that will be the MAMP PRO databases. # with the MAMP PRO servers running, these are equivalent: # /Applications/MAMP/Library/bin/mysql -h 127.0.0.1 other_options # and # /Applications/MAMP/Library/bin/mysql -h localhost other_options /Applications/MAMP/Library/bin/mysql mysql_options # enter. opens an interactive mysql session mysql> some command; # don't forget the semicolon mysql> exit; Create a local database # with the MAMP PRO servers running # replace `username` with your username, which is `root` by default /Applications/MAMP/Library/bin/mysql -h localhost -u username -p -e "create database database_name" or # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p # and then enter mysql> create database database_name; # don't forget the semicolon mysql> exit MAMP PRO's databases are stored in /Library/Application Support/appsolute/MAMP PRO/db so to confirm that it worked you can ls /Library/Application Support/appsolute/MAMP PRO/db # will output the available mysql versions. For example I have mysql56_2018-11-05_16-25-13 mysql57 # If it isn't clear which one you're after, open the main MAMP PRO and click # on the MySQL "servers and services" item. In my case it shows "Version: 5.7.26" # Now look in the relevant MySQL directory ls /Library/Application Support/appsolute/MAMP PRO/db/mysql57 # the newly created database should be in the list Delete a local database # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p -e "drop database database_name" Export a dump of a local database. Note that this uses mysqldump not mysql. # to export an uncompressed file # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysqldump -h localhost -u username -p database_name > the/output/path.sql # to export a compressed file # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysqldump -h localhost -u username -p database_name | gzip -c > the/output/path.gz Export a local dump from an external database over SSH. Note that this uses mysqldump not mysql. # replace `ssh-user`, `ssh_host`, `mysql_user`, `database_name`, and the output path # to end up with an uncompressed file ssh ssh_user@ssh_host "mysqldump -u mysql_user -p database_name | gzip -c" | gunzip > the/output/path.sql # to end up with a compressed file ssh ssh_user@ssh_host "mysqldump -u mysql_user -p database_name | gzip -c" > the/output/path.gz Import a local database dump into a local database # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p database_name < the/dump/path.sql Import a local database dump into a remote database over SSH. Use care with this one. But if you are doing it with Sequel Pro —maybe you are copying a Craft site's database from a production server to a QA server— you might as well be able to do it on the command line. ssh ssh_user@ssh_host "mysql -u username -p remote_database_name" < the/local/dump/path.sql For me, using the command line instead of the MAMP PRO and Sequel Pro GUI means less switching between keyboard and mouse, less opening up GUI features that aren't typically visible on my screen, and generally better DX. Give it a try! And while MAMP Pro's CLI is limited to the essentials, command line mysql of course knows no limits. If there's something else you use Sequel Pro for, you may be able to come up with a mysql CLI equivalent you like even better. Full Article Code Front-end Engineering Back-end Engineering
mp Employer sponsored temporary work visas (482 and 457) and Coronavirus (COVID-19) By feedproxy.google.com Published On :: Thu, 16 Apr 2020 20:10:00 +0000 If you’re a Temporary Skill Shortage visa holder – what should you do if you have been stood down or your work hours are reduced by your employer? The Australian Government has announced that Temporary Skill Shortage visa holders who have been stood down, but not laid off, will maintain their visa validity and businesses […] The post Employer sponsored temporary work visas (482 and 457) and Coronavirus (COVID-19) appeared first on Visa Australia - Immigration Lawyers & Registered Migration Agents. Full Article Employer Sponsored Visas AGEE Australian Government Endorsed Agreement Event breach visa conditions business employers Coronavirus covid-19 covid-19 pandemic temporary activity visa employer obligations Employer sponsored temporary work visa extend your visa extended visa labour agreement nominated salary reduced work hours sc 408 sc 457 sc 482 stood down subclass 408 Subclass 457 subclass 482 Temporary skill shortage TSS visa conditions visa holder visa validity
mp What’s New in Recipe Card Blocks Free & PRO By feedproxy.google.com Published On :: Thu, 16 Jan 2020 14:49:30 +0000 It’s been a few months since the release of the Recipe Card Blocks PRO plugin, and our team continued to release new updates constantly for both versions of the plugin: Free & PRO. As we’ve released many major updates for both versions in the past months, let’s make a quick overview of the most important changes and new features we’ve […] Full Article Plugins
mp Reel 3.0: New Color Schemes, Portfolio Styles & More! By feedproxy.google.com Published On :: Mon, 17 Feb 2020 08:13:49 +0000 We’re very excited to announce a new major update for our Reel theme. The new 3.0 version brings new color schemes and many improvements to the Portfolio Showcase widget. What’s new in 3.0? 5 New Color Schemes + 2 New Theme Styles Full-width header option New styles & options for Portfolio Showcase widget 5 New Color Schemes After long research […] Full Article News
mp Presence 2.0: Beaver Builder Integration, Dark Skin & More! By feedproxy.google.com Published On :: Wed, 08 Apr 2020 13:05:07 +0000 Great news for the users of Presence — our multipurpose theme. We have finally released the long-awaited 2.0 version, which features major changes and improvements. What’s new in Presence 2.0? Beaver Builder Integration Dark Skin New Demo: Organic Shop New Typography and Colors options in the Customizer New Templates in Page Builder Beaver Builder Integration If you have followed recent […] Full Article Theme Updates
mp Concurrency & Multithreading in iOS By feedproxy.google.com Published On :: Tue, 25 Feb 2020 08:00:00 -0500 Concurrency is the notion of multiple things happening at the same time. This is generally achieved either via time-slicing, or truly in parallel if multiple CPU cores are available to the host operating system. We've all experienced a lack of concurrency, most likely in the form of an app freezing up when running a heavy task. UI freezes don't necessarily occur due to the absence of concurrency — they could just be symptoms of buggy software — but software that doesn't take advantage of all the computational power at its disposal is going to create these freezes whenever it needs to do something resource-intensive. If you've profiled an app hanging in this way, you'll probably see a report that looks like this: Anything related to file I/O, data processing, or networking usually warrants a background task (unless you have a very compelling excuse to halt the entire program). There aren't many reasons that these tasks should block your user from interacting with the rest of your application. Consider how much better the user experience of your app could be if instead, the profiler reported something like this: Analyzing an image, processing a document or a piece of audio, or writing a sizeable chunk of data to disk are examples of tasks that could benefit greatly from being delegated to background threads. Let's dig into how we can enforce such behavior into our iOS applications. A Brief History In the olden days, the maximum amount of work per CPU cycle that a computer could perform was determined by the clock speed. As processor designs became more compact, heat and physical constraints started becoming limiting factors for higher clock speeds. Consequentially, chip manufacturers started adding additional processor cores on each chip in order to increase total performance. By increasing the number of cores, a single chip could execute more CPU instructions per cycle without increasing its speed, size, or thermal output. There's just one problem... How can we take advantage of these extra cores? Multithreading. Multithreading is an implementation handled by the host operating system to allow the creation and usage of n amount of threads. Its main purpose is to provide simultaneous execution of two or more parts of a program to utilize all available CPU time. Multithreading is a powerful technique to have in a programmer's toolbelt, but it comes with its own set of responsibilities. A common misconception is that multithreading requires a multi-core processor, but this isn't the case — single-core CPUs are perfectly capable of working on many threads, but we'll take a look in a bit as to why threading is a problem in the first place. Before we dive in, let's look at the nuances of what concurrency and parallelism mean using a simple diagram: In the first situation presented above, we observe that tasks can run concurrently, but not in parallel. This is similar to having multiple conversations in a chatroom, and interleaving (context-switching) between them, but never truly conversing with two people at the same time. This is what we call concurrency. It is the illusion of multiple things happening at the same time when in reality, they're switching very quickly. Concurrency is about dealing with lots of things at the same time. Contrast this with the parallelism model, in which both tasks run simultaneously. Both execution models exhibit multithreading, which is the involvement of multiple threads working towards one common goal. Multithreading is a generalized technique for introducing a combination of concurrency and parallelism into your program. The Burden of Threads A modern multitasking operating system like iOS has hundreds of programs (or processes) running at any given moment. However, most of these programs are either system daemons or background processes that have very low memory footprint, so what is really needed is a way for individual applications to make use of the extra cores available. An application (process) can have many threads (sub-processes) operating on shared memory. Our goal is to be able to control these threads and use them to our advantage. Historically, introducing concurrency to an app has required the creation of one or more threads. Threads are low-level constructs that need to be managed manually. A quick skim through Apple's Threaded Programming Guide is all it takes to see how much complexity threaded code adds to a codebase. In addition to building an app, the developer has to: Responsibly create new threads, adjusting that number dynamically as system conditions change Manage them carefully, deallocating them from memory once they have finished executing Leverage synchronization mechanisms like mutexes, locks, and semaphores to orchestrate resource access between threads, adding even more overhead to application code Mitigate risks associated with coding an application that assumes most of the costs associated with creating and maintaining any threads it uses, and not the host OS This is unfortunate, as it adds enormous levels of complexity and risk without any guarantees of improved performance. Grand Central Dispatch iOS takes an asynchronous approach to solving the concurrency problem of managing threads. Asynchronous functions are common in most programming environments, and are often used to initiate tasks that might take a long time, like reading a file from the disk, or downloading a file from the web. When invoked, an asynchronous function executes some work behind the scenes to start a background task, but returns immediately, regardless of how long the original task might takes to actually complete. A core technology that iOS provides for starting tasks asynchronously is Grand Central Dispatch (or GCD for short). GCD abstracts away thread management code and moves it down to the system level, exposing a light API to define tasks and execute them on an appropriate dispatch queue. GCD takes care of all thread management and scheduling, providing a holistic approach to task management and execution, while also providing better efficiency than traditional threads. Let's take a look at the main components of GCD: What've we got here? Let's start from the left: DispatchQueue.main: The main thread, or the UI thread, is backed by a single serial queue. All tasks are executed in succession, so it is guaranteed that the order of execution is preserved. It is crucial that you ensure all UI updates are designated to this queue, and that you never run any blocking tasks on it. We want to ensure that the app's run loop (called CFRunLoop) is never blocked in order to maintain the highest framerate. Subsequently, the main queue has the highest priority, and any tasks pushed onto this queue will get executed immediately. DispatchQueue.global: A set of global concurrent queues, each of which manage their own pool of threads. Depending on the priority of your task, you can specify which specific queue to execute your task on, although you should resort to using default most of the time. Because tasks on these queues are executed concurrently, it doesn't guarantee preservation of the order in which tasks were queued. Notice how we're not dealing with individual threads anymore? We're dealing with queues which manage a pool of threads internally, and you will shortly see why queues are a much more sustainable approach to multhreading. Serial Queues: The Main Thread As an exercise, let's look at a snippet of code below, which gets fired when the user presses a button in the app. The expensive compute function can be anything. Let's pretend it is post-processing an image stored on the device. import UIKit class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { compute() } private func compute() -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } At first glance, this may look harmless, but if you run this inside of a real app, the UI will freeze completely until the loop is terminated, which will take... a while. We can prove it by profiling this task in Instruments. You can fire up the Time Profiler module of Instruments by going to Xcode > Open Developer Tool > Instruments in Xcode's menu options. Let's look at the Threads module of the profiler and see where the CPU usage is highest. We can see that the Main Thread is clearly at 100% capacity for almost 5 seconds. That's a non-trivial amount of time to block the UI. Looking at the call tree below the chart, we can see that the Main Thread is at 99.9% capacity for 4.43 seconds! Given that a serial queue works in a FIFO manner, tasks will always complete in the order in which they were inserted. Clearly the compute() method is the culprit here. Can you imagine clicking a button just to have the UI freeze up on you for that long? Background Threads How can we make this better? DispatchQueue.global() to the rescue! This is where background threads come in. Referring to the GCD architecture diagram above, we can see that anything that is not the Main Thread is a background thread in iOS. They can run alongside the Main Thread, leaving it fully unoccupied and ready to handle other UI events like scrolling, responding to user events, animating etc. Let's make a small change to our button click handler above: class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { DispatchQueue.global(qos: .userInitiated).async { [unowned self] in self.compute() } } private func compute() -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } Unless specified, a snippet of code will usually default to execute on the Main Queue, so in order to force it to execute on a different thread, we'll wrap our compute call inside of an asynchronous closure that gets submitted to the DispatchQueue.global queue. Keep in mind that we aren't really managing threads here. We're submitting tasks (in the form of closures or blocks) to the desired queue with the assumption that it is guaranteed to execute at some point in time. The queue decides which thread to allocate the task to, and it does all the hard work of assessing system requirements and managing the actual threads. This is the magic of Grand Central Dispatch. As the old adage goes, you can't improve what you can't measure. So we measured our truly terrible button click handler, and now that we've improved it, we'll measure it once again to get some concrete data with regards to performance. Looking at the profiler again, it's quite clear to us that this is a huge improvement. The task takes an identical amount of time, but this time, it's happening in the background without locking up the UI. Even though our app is doing the same amount of work, the perceived performance is much better because the user will be free to do other things while the app is processing. You may have noticed that we accessed a global queue of .userInitiated priority. This is an attribute we can use to give our tasks a sense of urgency. If we run the same task on a global queue of and pass it a qos attribute of background , iOS will think it's a utility task, and thus allocate fewer resources to execute it. So, while we don't have control over when our tasks get executed, we do have control over their priority. A Note on Main Thread vs. Main Queue You might be wondering why the Profiler shows "Main Thread" and why we're referring to it as the "Main Queue". If you refer back to the GCD architecture we described above, the Main Queue is solely responsible for managing the Main Thread. The Dispatch Queues section in the Concurrency Programming Guide says that "the main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread. Because it runs on your application’s main thread, the main queue is often used as a key synchronization point for an application." The terms "execute on the Main Thread" and "execute on the Main Queue" can be used interchangeably. Concurrent Queues So far, our tasks have been executed exclusively in a serial manner. DispatchQueue.main is by default a serial queue, and DispatchQueue.global gives you four concurrent dispatch queues depending on the priority parameter you pass in. Let's say we want to take five images, and have our app process them all in parallel on background threads. How would we go about doing that? We can spin up a custom concurrent queue with an identifier of our choosing, and allocate those tasks there. All that's required is the .concurrent attribute during the construction of the queue. class ViewController: UIViewController { let queue = DispatchQueue(label: "com.app.concurrentQueue", attributes: .concurrent) let images: [UIImage] = [UIImage].init(repeating: UIImage(), count: 5) @IBAction func handleTap(_ sender: Any) { for img in images { queue.async { [unowned self] in self.compute(img) } } } private func compute(_ img: UIImage) -> Void { // Pretending to post-process a large image. var counter = 0 for _ in 0..<9999999 { counter += 1 } } } Running that through the profiler, we can see that the app is now spinning up 5 discrete threads to parallelize a for-loop. Parallelization of N Tasks So far, we've looked at pushing computationally expensive task(s) onto background threads without clogging up the UI thread. But what about executing parallel tasks with some restrictions? How can Spotify download multiple songs in parallel, while limiting the maximum number up to 3? We can go about this in a few ways, but this is a good time to explore another important construct in multithreaded programming: semaphores. Semaphores are signaling mechanisms. They are commonly used to control access to a shared resource. Imagine a scenario where a thread can lock access to a certain section of the code while it executes it, and unlocks after it's done to let other threads execute the said section of the code. You would see this type of behavior in database writes and reads, for example. What if you want only one thread writing to a database and preventing any reads during that time? This is a common concern in thread-safety called Readers-writer lock. Semaphores can be used to control concurrency in our app by allowing us to lock n number of threads. let kMaxConcurrent = 3 // Or 1 if you want strictly ordered downloads! let semaphore = DispatchSemaphore(value: kMaxConcurrent) let downloadQueue = DispatchQueue(label: "com.app.downloadQueue", attributes: .concurrent) class ViewController: UIViewController { @IBAction func handleTap(_ sender: Any) { for i in 0..<15 { downloadQueue.async { [unowned self] in // Lock shared resource access semaphore.wait() // Expensive task self.download(i + 1) // Update the UI on the main thread, always! DispatchQueue.main.async { tableView.reloadData() // Release the lock semaphore.signal() } } } } func download(_ songId: Int) -> Void { var counter = 0 // Simulate semi-random download times. for _ in 0..<Int.random(in: 999999...10000000) { counter += songId } } } Notice how we've effectively restricted our download system to limit itself to k number of downloads. The moment one download finishes (or thread is done executing), it decrements the semaphore, allowing the managing queue to spawn another thread and start downloading another song. You can apply a similar pattern to database transactions when dealing with concurrent reads and writes. Semaphores usually aren't necessary for code like the one in our example, but they become more powerful when you need to enforce synchronous behavior whille consuming an asynchronous API. The above could would work just as well with a custom NSOperationQueue with a maxConcurrentOperationCount, but it's a worthwhile tangent regardless. Finer Control with OperationQueue GCD is great when you want to dispatch one-off tasks or closures into a queue in a 'set-it-and-forget-it' fashion, and it provides a very lightweight way of doing so. But what if we want to create a repeatable, structured, long-running task that produces associated state or data? And what if we want to model this chain of operations such that they can be cancelled, suspended and tracked, while still working with a closure-friendly API? Imagine an operation like this: This would be quite cumbersome to achieve with GCD. We want a more modular way of defining a group of tasks while maintaining readability and also exposing a greater amount of control. In this case, we can use Operation objects and queue them onto an OperationQueue, which is a high-level wrapper around DispatchQueue. Let's look at some of the benefits of using these abstractions and what they offer in comparison to the lower-level GCI API: You may want to create dependencies between tasks, and while you could do this via GCD, you're better off defining them concretely as Operation objects, or units of work, and pushing them onto your own queue. This would allow for maximum reusability since you may use the same pattern elsewhere in an application. The Operation and OperationQueue classes have a number of properties that can be observed, using KVO (Key Value Observing). This is another important benefit if you want to monitor the state of an operation or operation queue. Operations can be paused, resumed, and cancelled. Once you dispatch a task using Grand Central Dispatch, you no longer have control or insight into the execution of that task. The Operation API is more flexible in that respect, giving the developer control over the operation's life cycle. OperationQueue allows you to specify the maximum number of queued operations that can run simultaneously, giving you a finer degree of control over the concurrency aspects. The usage of Operation and OperationQueue could fill an entire blog post, but let's look at a quick example of what modeling dependencies looks like. (GCD can also create dependencies, but you're better off dividing up large tasks into a series of composable sub-tasks.) In order to create a chain of operations that depend on one another, we could do something like this: class ViewController: UIViewController { var queue = OperationQueue() var rawImage = UIImage? = nil let imageUrl = URL(string: "https://example.com/portrait.jpg")! @IBOutlet weak var imageView: UIImageView! let downloadOperation = BlockOperation { let image = Downloader.downloadImageWithURL(url: imageUrl) OperationQueue.main.async { self.rawImage = image } } let filterOperation = BlockOperation { let filteredImage = ImgProcessor.addGaussianBlur(self.rawImage) OperationQueue.main.async { self.imageView = filteredImage } } filterOperation.addDependency(downloadOperation) [downloadOperation, filterOperation].forEach { queue.addOperation($0) } } So why not opt for a higher level abstraction and avoid using GCD entirely? While GCD is ideal for inline asynchronous processing, Operation provides a more comprehensive, object-oriented model of computation for encapsulating all of the data around structured, repeatable tasks in an application. Developers should use the highest level of abstraction possible for any given problem, and for scheduling consistent, repeated work, that abstraction is Operation. Other times, it makes more sense to sprinkle in some GCD for one-off tasks or closures that we want to fire. We can mix both OperationQueue and GCD to get the best of both worlds. The Cost of Concurrency DispatchQueue and friends are meant to make it easier for the application developer to execute code concurrently. However, these technologies do not guarantee improvements to the efficiency or responsiveness in an application. It is up to you to use queues in a manner that is both effective and does not impose an undue burden on other resources. For example, it's totally viable to create 10,000 tasks and submit them to a queue, but doing so would allocate a nontrivial amount of memory and introduce a lot of overhead for the allocation and deallocation of operation blocks. This is the opposite of what you want! It's best to profile your app thoroughly to ensure that concurrency is enhancing your app's performance and not degrading it. We've talked about how concurrency comes at a cost in terms of complexity and allocation of system resources, but introducing concurrency also brings a host of other risks like: Deadlock: A situation where a thread locks a critical portion of the code and can halt the application's run loop entirely. In the context of GCD, you should be very careful when using the dispatchQueue.sync { } calls as you could easily get yourself in situations where two synchronous operations can get stuck waiting for each other. Priority Inversion: A condition where a lower priority task blocks a high priority task from executing, which effectively inverts their priorities. GCD allows for different levels of priority on its background queues, so this is quite easily a possibility. Producer-Consumer Problem: A race condition where one thread is creating a data resource while another thread is accessing it. This is a synchronization problem, and can be solved using locks, semaphores, serial queues, or a barrier dispatch if you're using concurrent queues in GCD. ...and many other sorts of locking and data-race conditions that are hard to debug! Thread safety is of the utmost concern when dealing with concurrency. Parting Thoughts + Further Reading If you've made it this far, I applaud you. Hopefully this article gives you a lay of the land when it comes to multithreading techniques on iOS, and how you can use some of them in your app. We didn't get to cover many of the lower-level constructs like locks, mutexes and how they help us achieve synchronization, nor did we get to dive into concrete examples of how concurrency can hurt your app. We'll save those for another day, but you can dig into some additional reading and videos if you're eager to dive deeper. Building Concurrent User Interfaces on iOS (WWDC 2012) Concurrency and Parallelism: Understanding I/O Apple's Official Concurrency Programming Guide Mutexes and Closure Capture in Swift Locks, Thread Safety, and Swift Advanced NSOperations (WWDC 2015) NSHipster: NSOperation Full Article Code
mp CLI Equivalents for Common MAMP PRO and Sequel Pro Tasks By feedproxy.google.com Published On :: Thu, 26 Mar 2020 00:00:00 -0400 Working on website front ends I sometimes use MAMP PRO to manage local hosts and Sequel Pro to manage databases. Living primarily in my text editor, a terminal, and a browser window, moving to these click-heavy dedicated apps can feel clunky. Happily, the tasks I have most frequently turned to those apps for —starting and stopping servers, creating new hosts, and importing, exporting, deleting, and creating databases— can be done from the command line. I still pull up MAMP PRO if I need to change a host's PHP version or work with its other more specialized settings, or Sequel Pro to quickly inspect a database, but for the most part I can stay on the keyboard and in my terminal. Here's how: Command Line MAMP PRO You can start and stop MAMP PRO's servers from the command line. You can even do this when the MAMP PRO desktop app isn't open. Note: MAMP PRO's menu icon will not change color to reflect the running/stopped status when the status is changed via the command line. Start the MAMP PRO servers: /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd startServers Stop the MAMP PRO servers: /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd stopServers Create a host (replace host_name and root_path): /Applications/MAMP PRO.app/Contents/MacOS/MAMP PRO cmd createHost host_name root_path MAMP PRO-friendly Command Line Sequel Pro Note: if you don't use MAMP PRO, just replace the /Applications/MAMP/Library/bin/mysql with mysql. In all of the following commands, replace username with your user name (locally this is likely root) and database_name with your database name. The -p (password) flag with no argument will trigger an interactive password prompt. This is more secure than including your password in the command itself (like -pYourPasswordHere). Of course, if you're using the default password root is not particular secure to begin with so you might just do -pYourPasswordHere. Setting the -h (host) flag to localhost or 127.0.0.1 tells mysql to look at what's on localhost. With the MAMP PRO servers running, that will be the MAMP PRO databases. # with the MAMP PRO servers running, these are equivalent: # /Applications/MAMP/Library/bin/mysql -h 127.0.0.1 other_options # and # /Applications/MAMP/Library/bin/mysql -h localhost other_options /Applications/MAMP/Library/bin/mysql mysql_options # enter. opens an interactive mysql session mysql> some command; # don't forget the semicolon mysql> exit; Create a local database # with the MAMP PRO servers running # replace `username` with your username, which is `root` by default /Applications/MAMP/Library/bin/mysql -h localhost -u username -p -e "create database database_name" or # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p # and then enter mysql> create database database_name; # don't forget the semicolon mysql> exit MAMP PRO's databases are stored in /Library/Application Support/appsolute/MAMP PRO/db so to confirm that it worked you can ls /Library/Application Support/appsolute/MAMP PRO/db # will output the available mysql versions. For example I have mysql56_2018-11-05_16-25-13 mysql57 # If it isn't clear which one you're after, open the main MAMP PRO and click # on the MySQL "servers and services" item. In my case it shows "Version: 5.7.26" # Now look in the relevant MySQL directory ls /Library/Application Support/appsolute/MAMP PRO/db/mysql57 # the newly created database should be in the list Delete a local database # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p -e "drop database database_name" Export a dump of a local database. Note that this uses mysqldump not mysql. # to export an uncompressed file # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysqldump -h localhost -u username -p database_name > the/output/path.sql # to export a compressed file # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysqldump -h localhost -u username -p database_name | gzip -c > the/output/path.gz Export a local dump from an external database over SSH. Note that this uses mysqldump not mysql. # replace `ssh-user`, `ssh_host`, `mysql_user`, `database_name`, and the output path # to end up with an uncompressed file ssh ssh_user@ssh_host "mysqldump -u mysql_user -p database_name | gzip -c" | gunzip > the/output/path.sql # to end up with a compressed file ssh ssh_user@ssh_host "mysqldump -u mysql_user -p database_name | gzip -c" > the/output/path.gz Import a local database dump into a local database # with the MAMP PRO servers running # replace `username` (`root` by default) and `database_name` /Applications/MAMP/Library/bin/mysql -h localhost -u username -p database_name < the/dump/path.sql Import a local database dump into a remote database over SSH. Use care with this one. But if you are doing it with Sequel Pro —maybe you are copying a Craft site's database from a production server to a QA server— you might as well be able to do it on the command line. ssh ssh_user@ssh_host "mysql -u username -p remote_database_name" < the/local/dump/path.sql For me, using the command line instead of the MAMP PRO and Sequel Pro GUI means less switching between keyboard and mouse, less opening up GUI features that aren't typically visible on my screen, and generally better DX. Give it a try! And while MAMP Pro's CLI is limited to the essentials, command line mysql of course knows no limits. If there's something else you use Sequel Pro for, you may be able to come up with a mysql CLI equivalent you like even better. Full Article Code Front-end Engineering Back-end Engineering
mp Internationaal Symposium 2012 By designworkplan.com Published On :: Fri, 14 Dec 2012 11:15:30 +0000 Een unieke dag in London met sprekers over ontwerp, innovatie en samenwerking. Vanuit verschillende oogpunten worden de onderwerpen besproken in uitdagende sessies met grote interactie met het publiek. Full Article blog nl SEGD
mp Book review: Orientation & Identity By designworkplan.com Published On :: Wed, 26 Jun 2013 09:00:31 +0000 Interviews and background stories covered in this book: Orientation & Identity by Erwin K. Bauer. Full Article review blogen en Orientation & Identity
mp Deliver a Great Message, Simply By traceygrady.com Published On :: Tue, 26 May 2015 23:31:46 +0000 A poster campaign has recently caught my attention and I’m impressed by the impact of the message it contains. The elements that work well here can be as relevant to business. Full Article Communicate communication content strategy creativity Print Design street art trends
mp There is no longer any such thing as Computer Security By blog.codinghorror.com Published On :: Fri, 21 Sep 2018 09:50:53 GMT Remember "cybersecurity"? Mysterious hooded computer guys doing mysterious hooded computer guy .. things! Who knows what kind of naughty digital mischief they might be up to? Unfortunately, we now live in a world where this kind of digital mischief is literally rewriting the world's history. For proof of that, Full Article
mp The Cloud Is Just Someone Else's Computer By blog.codinghorror.com Published On :: Sun, 17 Feb 2019 02:15:26 GMT When we started Discourse in 2013, our server requirements were high: 1GB RAM modern, fast dual core CPU speedy solid state drive with 20+ GB I'm not talking about a cheapo shared cpanel server, either, I mean a dedicated virtual private server with those specifications. We were OK with that, Full Article
mp Creating a Block-based Theme Using Block Templates By feedproxy.google.com Published On :: Tue, 21 Jan 2020 18:06:19 +0000 This post outlines the steps I took to create a block-based theme version of Twenty Twenty. Thanks to Kjell Reigstad for helping develop the theme and write this post. There’s been a lot of conversation around how theme development changes as Full Site Editing using Gutenberg becomes a reality. Block templates are an experimental feature … Continue reading "Creating a Block-based Theme Using Block Templates" Full Article Education Theme Development Themes Block Templates Featured Content Gutenberg Starter Themes TwentyTwenty
mp New Branding & Website Design Launched for Enterprise High School in Clearwater, Florida By feedproxy.google.com Published On :: Wed, 13 Jul 2016 20:03:45 +0000 We recently completed a full rebrand and website design project for Enterprise High School, a charter school located in Clearwater,...continue reading Full Article Featured Website Launches Web Design wordpress
mp Logo Design & Branding for Food Launcher By feedproxy.google.com Published On :: Wed, 16 May 2018 13:57:16 +0000 A startup specializing in food product development and commercialization services, “Food Launcher” is a team of food scientists with over...continue reading Full Article Featured Graphic / Print Design Logo Design Southwest Florida
mp Good Cop & Bad Cop: Laying Down the Law and Keeping People Happy As an Independent Business Owner By feedproxy.google.com Published On :: Sat, 28 Apr 2012 00:51:13 +0000 Earlier this week I met up for coffee with a client of mine. The two of us originally met when his employeer was my client and after leaving that job he hired me to customize his personal blog and we formed our own client/designer relationship. I was excited when he emailed me last week with the […] Full Article Business Clients
mp She’s Geeky: My First Unconference & Having Feels about Solidarity Between Women in Tech By feedproxy.google.com Published On :: Sun, 11 May 2014 21:07:29 +0000 This Friday I attended the first day of She’s Geeky here in Seattle. It was my first experience of the Unconference Format and I had no idea what to expect, but ended up having a GREAT TIME. Discussions that I joined in on throughout the day included subjects such as Impostor Syndrome, Diversity Groups, Side- […] Full Article Community Liz
mp Design Sprints Complete Guide By blog.useberry.com Published On :: Sat, 09 May 2020 04:15:16 PDT https://blog.useberry.com/design-sprints-complete-guide/ Full Article
mp Trump officials say people with disabilities must not be denied lifesaving coronavirus care By feedproxy.google.com Published On :: Mon, 30 Mar 2020 14:09:48 EDT Patients with disabilities must receive the same level of lifesaving medical treatment from hospitals during the coronavirus pandemic as able-bodied patients, the Trump administration said. Full Article
mp Want to help the USPS and vets? Buy a 'Healing PTSD' stamp By feedproxy.google.com Published On :: Thu, 30 Apr 2020 12:02:25 EDT Support two entities with the price of one. Full Article
mp All The Vintage Logo Template Designs You Could Ever Wish For! By blog.spoongraphics.co.uk Published On :: Mon, 20 Apr 2020 07:00:23 +0000 Creating vintage style designs for company brands, T-shirts, packaging designs, book covers, window signs, and countless other graphic design project scenarios has never been easier with this huge bundle of 200 editable logo templates from Heritage Type Co. Logo Templates are ready-made designs that can be easily customised to quickly create stunning badges and emblems […] The post All The Vintage Logo Template Designs You Could Ever Wish For! appeared first on Spoon Graphics. Full Article News deal heritage type logo templates vintage vintage logo designs vintage logos
mp Want to help the USPS and vets? Buy a 'Healing PTSD' stamp By feedproxy.google.com Published On :: Thursday, April 30, 2020 - 12:02pm Support two entities with the price of one. Full Article
mp 5 Important Tips When Building Your First Mobile App By feedproxy.google.com Published On :: Tue, 07 Apr 2020 07:50:35 +0000 Building a mobile application is a complex process, and mistake can be costly in time and money. To make sure that your mobile app projects are a success, here are a few tips that will be helpful. 1. Plan Ahead When building anything complex, you should never start without a plan. Building a mobile app […] Full Article Blog How-to & tutorials App design Web design
mp How to Improve User Experience Design: Tips to Increase Conversion Rates By feedproxy.google.com Published On :: Fri, 24 Apr 2020 13:53:03 +0000 No one can deny that UX (user experience) is the foundation of any website. This is the main reason why many website owners always look for ways to improve it. Some even spend a lot of money on design because of it. So, what is UX design exactly? And what is the difference between user […] Full Article Blog How-to & tutorials UX Web design
mp 9 Simple CSS Image Filters By webdesignerwall.com Published On :: Thu, 01 Dec 2016 11:00:05 +0000 CSS image filters are a quick way to tweak images in the browser without resorting to Photoshop. This simple reference gives you 9 CSS filter shorthands that provide an excellent way to maintain style consistency across visual content on your site, or just add a little fun to image hovers. CSS filters are most commonly […] The post 9 Simple CSS Image Filters appeared first on Web Designer Wall. Full Article Tutorials CSS
mp 75+ High Quality Free Fonts: Handwriting, Script & Brush Fonts By webdesignerwall.com Published On :: Mon, 12 Dec 2016 13:00:05 +0000 Fonts took on a revival in handmade styles this year, from calligraphic, script and handwritten to brush painted and block-printed. Combined with the great visual appeal of hero images and typographic layouts in web design, handwriting fonts are a trend that you can expect to see more of. In this article you’ll find a fresh […] The post 75+ High Quality Free Fonts: Handwriting, Script & Brush Fonts appeared first on Web Designer Wall. Full Article Design Trends Featured Fonts Freebies
mp You Might Be Tempted to Use These CSS Tricks But Should You? By webdesignerwall.com Published On :: Wed, 14 Dec 2016 14:00:00 +0000 The temptation to dive right into new and exciting CSS tricks is strong – you might even do it without knowing it through learning by example or implementing that hot new framework everyone is talking about. It is more important to fully understand new CSS classes and properties, experiment with them and learn their limitations […] The post You Might Be Tempted to Use These CSS Tricks But Should You? appeared first on Web Designer Wall. Full Article Tutorials CSS
mp Design Trends in 2016 & What to Expect in 2017 By webdesignerwall.com Published On :: Tue, 27 Dec 2016 13:00:10 +0000 The year ahead should be a really exciting one for design, with the introduction of wider support for SVG and a host of new things we can do with CSS as CSS4 gains broader acceptance. In taking a look back at what made a big splash this year, I hope you will be inspired to […] The post Design Trends in 2016 & What to Expect in 2017 appeared first on Web Designer Wall. Full Article Design Trends Web Design
mp 30 Truly Interactive Websites Built With CSS & JavaScript By webdesignerwall.com Published On :: Tue, 03 Jan 2017 12:00:09 +0000 All websites are somewhat interactive…we click on links or scroll a page, but truly interactive websites take us on a user-driven adventure or draws us in through motion and sound while giving us the power of choice. Interaction can be as simple as a series of clicks that navigate us through a story or landscape, […] The post 30 Truly Interactive Websites Built With CSS & JavaScript appeared first on Web Designer Wall. Full Article Design Trends Inspiration
mp Tutorial: Duo Layout With CSS3 Animations & Transitions (Pt. 2) By webdesignerwall.com Published On :: Thu, 05 Jan 2017 11:00:01 +0000 Last week I demonstrated how to build a split-screen website layout using CSS flexbox and viewport units that offers an alternative way to present a brand’s featured content. Clicking on one side or the other navigates further into the site without a page load, using CSS transitions and 3d transforms. This week, I’ll show you […] The post Tutorial: Duo Layout With CSS3 Animations & Transitions (Pt. 2) appeared first on Web Designer Wall. Full Article Tutorials CSS jQuery
mp Master CSS Flexbox in 5 Simple Steps By webdesignerwall.com Published On :: Thu, 12 Jan 2017 13:00:11 +0000 CSS flexbox is an incredibly useful tool for layout without using floats or positioning. Currently almost all current browser version support flexbox, making it a go-to standard for web design. It can solve a number of problems efficiently, such as vertical alignment, space distribution, ordering of elements and sizing. It offers more freedom to arrange […] The post Master CSS Flexbox in 5 Simple Steps appeared first on Web Designer Wall. Full Article Tutorials CSS
mp Easy CSS Animation With Transition & Transforms By webdesignerwall.com Published On :: Thu, 19 Jan 2017 13:00:43 +0000 Recently, I walked you through how to create a simple landing page that used a couple different CSS animation techniques. “Animation” is a loose term, in web design usually referring to anything that involves movement. CSS transitions are one tool we are given to manipulate elements on state changes or mouse events, and when combines […] The post Easy CSS Animation With Transition & Transforms appeared first on Web Designer Wall. Full Article Tutorials CSS
mp Best Email Marketing Tips to Increase Engagement & Subscribers By webdesignerwall.com Published On :: Thu, 09 Feb 2017 17:06:31 +0000 Email is your post powerful marketing channel when used well. Your visitor’s inbox is a perfect opportunity for you to capture attention, communicate important updates and invite readers back to your site for increased visibility. The stats on email marketing effectiveness say it all – top marketing specialists and service providers tell us that email […] The post Best Email Marketing Tips to Increase Engagement & Subscribers appeared first on Web Designer Wall. Full Article General Stuff markeing
mp Common WordPress Errors & How to Fix Them By webdesignerwall.com Published On :: Thu, 28 Feb 2019 11:00:47 +0000 WordPress is an amazingly stable platform thanks to the dedication and talent of the hundreds of professionals contributing to it, and the strict code standards they follow. Even so, the huge variety of themes, plugins and server environments out there make it difficult to guarantee nothing will ever go wrong. This guide will help you […] The post Common WordPress Errors & How to Fix Them appeared first on Web Designer Wall. Full Article Tutorials WordPress
mp Upper Yosemite Falls & Half Dome Moonbow By feedproxy.google.com Published On :: Tue, 16 May 2017 00:01:58 +0000 This past week was the optimal time to photograph moonbows in Yosemite Valley. I revisited photographing the moonbow at Upper Yosemite Falls as I had last year, but this time there was considerable more water and as a result the moonbow (rainbow by moonlight) was more easily seen. It was considerably larger, more vivid in color and wider arching. Conditions were great and at times a little too good as the 3 cameras I set up were completely drenched. If you’d like to read about what it took to get this photo be sure to check out my last blog post, Upper Yosemite Falls Moonbow – Getting The Shot, as it goes into a lot of detail about the hike and the challenges I faced. If you’re curious about gear and settings this was taken with a Canon 5D Mark IV and Canon 11-24mm f/4 lens. Settings were ISO 640, 15 seconds at f/4. Full Article Astro-landscape California Daily Photo Landscape Locations Photography Yosemite Moonbow Nature Night Photography Upper Yosemite Falls
mp How I Evaluate Crowdfunding Projects: Kickstarter, Indiegogo & Beyond By feedproxy.google.com Published On :: Tue, 01 Aug 2017 07:01:19 +0000 13 Questions You Should Ask Before Backing A Crowdfunding Project on Kickstarter, Indiegogo and Beyond Full Article Featured Photography Technology Tips & Tricks Crowdfunded Crowdfunding Indiegogo Kickstarter
mp Sony Xperia Z1 Compact Complete Guide By unlimitedcellphoneplansblog.wordpress.com Published On :: Mon, 07 Mar 2016 12:08:20 +0000 Have you got yourself a Sony Xperia Z1 Compact but not sure how to do something on it? Don’t worry, we’ve come up with a comprehensive guide for all the things your handset is capable of. Navigate the various sections using the links below. If you can’t find what you’re looking for just leave a … Continue reading Sony Xperia Z1 Compact Complete Guide → Full Article Uncategorized
mp Motorola Moto X complete guide By unlimitedcellphoneplansblog.wordpress.com Published On :: Mon, 07 Mar 2016 12:09:50 +0000 Got a brand new Motorola Moto X but not sure how to do something on it? Fear not as we’ve put together a comprehensive guide to get your started. Scroll through or navigate using the links below, and if you’ve got a query and we havne’t covered it, let us know. Leave us a comment … Continue reading Motorola Moto X complete guide → Full Article Uncategorized
mp Motorola Moto G Complete Guide By unlimitedcellphoneplansblog.wordpress.com Published On :: Mon, 07 Mar 2016 12:11:22 +0000 Got a Motorola Moto G but not sure how to do something on it? Not to worry as we’ve come up with a comprehensive guide for all the things your handset is capable of doing. Navigate using the links below or download the full guide by clicking on the PDF logo. If you can’t find … Continue reading Motorola Moto G Complete Guide → Full Article Uncategorized
mp Unwell: The Public Health Implications of Unregulated Drinking Water By feedproxy.google.com Published On :: Mon, 06 Nov 2017 21:28:26 +0000 By Nate Seltenrich Environmental Health Perspectives Roughly one in seven U.S. residents relies on a private well for drinking water.1 Unlike the rest of the population served by the nation’s many public water systems,2 these 44.5 million Americans are not … Continue reading → Full Article Drinking Water safe drinking water water wells
mp Top 15 Digital Scrapbooking Downloads (Free & Paid) By justcreative.com Published On :: Mon, 27 Apr 2020 02:07:13 +0000 Scrapbooking can be a fun way to capture important moments in life and with our list of the Top 15 Scrapbooking Resources, you can start right away! Full Article Tools & Gear DesignCuts Scrapbooking
mp How to Master the Elevator Pitch & Leave a Great First Impression By justcreative.com Published On :: Mon, 04 May 2020 06:12:24 +0000 Want to master the elevator pitch and leave a great first impression? Use this easy formula to help communicate what you do in a concise pitch! Full Article Business Elevator Pitch
mp Top 20 Photoshop Actions (Free & Premium) By justcreative.com Published On :: Tue, 05 May 2020 03:46:08 +0000 Photoshop is an amazing program and with our list of the 20 best free & premium Photoshop Actions, you can start creating incredible work immediately. Full Article Tools & Gear Actions Photoshop
mp The Strategic Pyramid – Brand Purpose, Mission, Vision & Values By justcreative.com Published On :: Fri, 08 May 2020 01:18:35 +0000 Do you know the difference between a mission and a vision? Or the difference between a purpose and a goal? Don’t worry, you’re not the only one. Let's clarify! Full Article Branding Brand Strategy
mp 7 Vital Components of a Successful Brand Strategy By justcreative.com Published On :: Fri, 08 May 2020 06:20:02 +0000 A brand strategy is a long-term plan that affects every facet of your business, but creating one can be confusing. We break it down into 7 essential components. Full Article Branding Guest Articles brBrand Strategy
mp Web Design & CRO – A Checklist for Designers By icanbecreative.com Published On :: Thu, 05 Mar 2020 12:35:50 PST Designing is often as much as a science as it is an art. Using specific knowledge of human psychology & online behaviour, the science of creating effective websites that are optimized for business... Full Article Learning