NewsWave Released

During the last couple of months I’ve been working on a new take on the RSS Reader.

With so many options out there (NetNewsWire, Unread, Reeder….), why would I go and develop another one? 

The main reason is I wanted to create an RSS reader that would be extremely accessible to use for the average user. I had many ideas on how that could be approached, but in the end I settled on the following four principles:

-Easy subscription to feeds thanks to a growing searchable directory. 

-One unified chronological twitter style timeline for posts.

-Private tokens to identify users, effectively making all users anonymous. 

-A central server to allow syncing, reduce data consumption and backup all information.

 

Here’s the first conceptualization of what would become the new app, things have changed here and there but it’s incredible how many similarities the final app has with the original concept:

NewsWave first sketch

 

One of the decisions I struggled the most with was the business model, in the end -considering also the ongoing server costs- I settled with:

1. One time in app purchase that creates the user token and gives them 30 days to try the app with all features. This is not a subscription and therefore will not auto-renew. 

2. After 30 days they may decide to (ideally) subscribe for a $ 19.9 / year or use the app for free in “Basic Mode” -limiting daily fetches to 3 and disabling bookmark & position sync between devices-. 

As for the App Icon, I decided to go with an “in house” option and -depending on the app reception- hire a designer to improve it. 

140x140

 

Stacks image 59ea79d 942x942 2x

 

That’s the story of NewsWave, you can grab it from the app store and find its website here

How is the app doing? I’ll post shortly about that. 

Comments / questions? I’m at @MarcMasVi 

 

Marc

Create UIButton in code (swift)

Adding and customizing UIButtons in storyboard is extremely easy (and I would recommend doing it that way whenever possible), but what if you need to do it yourself in code?

The process takes only couple of lines of code, but it can escalate quickly if you add multiple buttons. To keep code clean I’ve created a quick function to create them quickly:

    func createNewUIButton(title: String, textSizeDelta: CGFloat?, backgroundColor: UIColor?, cornerRadius: Bool, cornerColor: UIColor?) -> UIButton{

 

        let button = UIButton(type: .system)

        button.translatesAutoresizingMaskIntoConstraints = false

        button.setTitle(”  \(title)  “, for: .normal)

        button.titleLabel!.font = UIFont.systemFont(ofSize: UIFont.systemFontSize + (textSizeDelta ?? 0))

        if backgroundColor != nil{

            button.backgroundColor = backgroundColor!

        }

        if cornerRadius == true{

            button.layer.masksToBounds = true

            button.layer.cornerRadius = 7.5

            button.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMinYCorner]

            button.layer.borderWidth = 2

            button.layer.borderColor = cornerColor?.cgColor ?? UIColor.darkGray.cgColor

        }

       

        return button

 

    }

 

Once you get the button make sure you add constraints (if needed) and action and you’re good to go!

For reference, example of adding the action after you’ve instantiated the button:

button.addTarget(self, action: #selector(FUNCTION_GOES_HERE), for: .touchUpInside)

 

Questions / comments / suggestions? @MarcMasVi 

Marc

Animating UIView by changing constraints

That’s one of the most fun and easiest animations you can do.

First, let’s make sure we know what constraint to change: you’ll have to make sure you either have the constraint as an @IBOutlet or -if you’ve added in code- make sure you add an identifier to it. 

Then you’ll trigger a constant change within an animation block, in this first example we’re using the identifier to find the right constraint:

        UIView.animate(withDuration: 0.2) {

            let identifyTheRightConstraint = self.parent!.view.constraints.filter{ $0.identifier == “distanceOfMessageToTop” }

            if let constraint = identifyTheRightConstraint.first{

                constraint.constant = 0

            }

            self.parent!.view.layoutIfNeeded()

 

        }

 

Or this is you’re using an @IBOutlet

        UIView.animate(withDuration: 0.2) {

 

            IBOutletConstraint.constant = 0

 

            self.parent!.view.layoutIfNeeded()

 

        }

 

It’s very important that you’re triggering layoutIfNeeded() for the parent view of the view you’re changing the constraints on. And that’s it!  

Questions / comments / suggestions? @MarcMasVi 

Marc

Opening links in Safari / Embedded Safari – iOS

In iOS you can either do it with a push:

let safariVC = SFSafariViewController(url: URL(string: “www.google.com”)!, configuration: .init())

 

present(safariVC, animated: true, completion: nil)

 

or directly in the Safari app:

UIApplication.shared.open(URL(string:www.google.com”)!, options: [:]) { (_) in }

 

Very practical, 

 

Marc

Reloading/inserting Dynamic Height cells and keeping Scroll position

For an upcoming app I had to insert Dynamic Height cells while keeping the tableView offset -scrolling position- constant. This would typically be a trivial issue, but given that when using Dynamic Height cells nor the contentSize nor the offset can be trusted it’s a bit more tricky than it seems…

What I was trying to do was adding cells at the top of my tableView without any scroll taking place, similarly to what Twitter, Tweetbot or Twitterrific apps do. 

Screen Shot 2018 12 30 at 6 17 23 PM

Green cells in the “After” scenario show how the new cells have been inserted at the top, without affecting scrolling. 

The approach that ended up working smoothly was:

Step 1. Storing in a variable the UITableViewCell below the Navigation Bar and storing the offset of that cell in relation to the Navigation Bar. 

Step 2. Insert cells / reloadData.

Step 3.Scroll to the cell we saved before the insert/reload and then add the offset. 

Here’s the code:

 

        UIView.performWithoutAnimation {

            //Step 1 Detect & Store

            let topWillBeAt = getTopVisibleRow() + countOfAddedItems

            let oldHeightDifferenceBetweenTopRowAndNavBar = heightDifferenceBetweenTopRowAndNavBar()

            

            //Step 2 Insert

            self.tableView.insertRows(at: arrayOfIndexPaths, with: .none)

            

            //Step 3 Restore Scrolling

            tableView.scrollToRow(at: IndexPath(row: topWillBeAt, section: 0), at: .top, animated: false)

            tableView.contentOffset.y = tableView.contentOffset.y – oldHeightDifferenceBetweenTopRowAndNavBar

 

        }

 

And supporting functions:

    func getTopVisibleRow () -> Int {

        //We need this to accounts for the translucency below the nav bar

        let navBar = navigationController?.navigationBar

        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)

        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height + 1)

        let accurateIndexPath = tableView.indexPathForRow(at: pointWhereNavBarEnds)

        return accurateIndexPath?.row ?? 0

    }

    

    func heightDifferenceBetweenTopRowAndNavBar()-> CGFloat{

        let rectForTopRow = tableView.rectForRow(at:IndexPath(row:  getTopVisibleRow(), section: 0))

        let navBar = navigationController?.navigationBar

        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)

        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height)

        let differenceBetweenTopRowAndNavBar = rectForTopRow.origin.y – pointWhereNavBarEnds.y

        return differenceBetweenTopRowAndNavBar

 

    }

 

Questions / comments / suggestions? @MarcMasVi 

PS. On another topic, if you get some jumpy scrolling with Dynamic Height cells I strongly suggest you to look into this question from Stack Overflow. 

Marc

Measuring function execution time

This is a very quick one. For an app I’m working on I wanted to measure how long it took a given function to complete. 

I typically don’t do this, as most functions are very fast, but this one involved a server fetch and some background processing. Anyhow, I’m getting distracted, here it is -easy as pie-:

 

let startTime = Date()

 

//Code

 

let endTime = Date()

let timeElapsed: Double = endTime.timeIntervalSince(startTime)

 

print(“Time taken \(timeElapsed) seconds”)

 

Questions / comments / suggestions? I’m at @MarcMasVi 

Marc

 

 

Dealing with UIGraphics ImageContext ‘Memory Leaks’

Here’s the code to change the image size of an image in iOS so that then it can be saved with smaller footprint:

let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

        

// Actually do the resizing to the rect using the ImageContext stuff

UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)

image.draw(in: rect)

        

let newImage = UIGraphicsGetImageFromCurrentImageContext()

 

UIGraphicsEndImageContext()

 

This works great if you do a few size conversions, however if you do hundreds or thousands you’ll see that “UIGraphicsGetImageFromCurrentImageContext” will hold onto the image in memory until the code returns control to the runloop, which may not happen for a long time. This may lead to crashes when iOS decides your app is using too much memory. Here’s what was happening to my app:

Screen Shot 2018 10 06 at 11 10 25 AM

To solve this I’m using a new autorelease pool at every cycle forcing it to release the memory every time. Works great, here’s the implementation of the autorelease pool code:

private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage {

        return autoreleasepool { () -> UIImage in

             […]

             return newImage!

        }

 

    }

And the implementation of the full function for reference:

    private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage {

        return autoreleasepool { () -> UIImage in

            let toWidth = image.size.width * (toHeight/image.size.height)

            let targetSize = CGSize(width: toWidth, height: toHeight)

            let size = image.size

        

            let widthRatio  = targetSize.width  / size.width

            let heightRatio = targetSize.height / size.height

        

            // Orientation detection

            var newSize: CGSize

            if(widthRatio > heightRatio) {

            newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio)

            } else {

            newSize = CGSize(width: size.width * widthRatio,  height: size.height * widthRatio)

            }

        

            // Calculated rect

            let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)

        

            // Resize

            UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)

            image.draw(in: rect)

        

            let newImage = UIGraphicsGetImageFromCurrentImageContext()

            UIGraphicsEndImageContext()

        

            return newImage!

        }

        

 

    }

 

Comments / questions? I’m at @MarcMasVi 

 

Marc

Pre-Scrolling a Table to a specific cell at load

Although that’s something that appears fairly easy, the truth -for me at least-, is that scrolling a UITableView to a specific cell every time you load it may prove tricky. 

In the app I’m working on I implement it in the following way: every time a view appeared I assess if the top visible row is the one it should. If it is, no action is taken, else we’ll scroll to the right row. 

    override func viewDidAppear(_ animated: Bool) {

        super.viewDidAppear(animated)

        //Check if we need to scroll to a specific cell

        if getTopVisibleRow() != getTableLastViewedTopPosition() {

            tableView.scrollToRow(at: IndexPath(row: getTableLastViewedTopPosition(), section: 0), at: .top, animated: false)

        }

 

    }

For reference here’s what I’m doing in the getTopVisibleRow(), note that is is important only if you’re using a translucid navbar:

    func getTopVisibleRow () -> Int {

        //We need this to accounts for the translucency below the nav bar

        let navBar = navigationController?.navigationBar

        let whereIsNavBarInTableView = tableView.convert(navBar!.bounds, from: navBar)

        let pointWhereNavBarEnds = CGPoint(x: 0, y: whereIsNavBarInTableView.origin.y + whereIsNavBarInTableView.size.height + 1)

        let accurateIndexPath = tableView.indexPathForRow(at: pointWhereNavBarEnds)

        return accurateIndexPath?.row ?? 0

 

    }

For getTableLastViewedTopPosition() I’m simply fetching from the model what cell index should we be showing. 

Questions / comments? I’m at @MarcMasVi 

Marc

Find the right value in an Array, or in an Array of Dictionaries

From time to time I discover a new way of coding something that significantly improves readability & minimizes how much I need to code. 

Here’s one of such latest discoveries. Typically to find a value in an array (or in an array of dictionaries) with a specific condition I would do a for loop with an if statement checking for the right value. This works great of course, but its a lot of code to do something that simple. 

Here’s a simple way to find a value in an array of dictionaries:

let matchingArticle = arrayOfItems.first(where: {$0.exampleKey == 3843})

 

We ask the arrayOfItems to give us the first value where the dictionary key exampleKey takes the value 3843. 

And if it’s only an array of int or string, for example, is even easier:

 let matchingArticle = arrayOfItems.first(where: {$0 == 859})

We simply ask the arrayOfItems to give us the first value where the its value equals 859. 

Note, in this code example we assume you will always have only up to 1 match. 

I’ve come to love this function and I think you will too. 

 

Questions / comments / suggestions? I’m at @MarcMasVi 

Marc

 

Inserting cells at the top of a UITableView with no scrolling

If you would like your table to be refreshed “ala Twitter”, with new data being added on top, here’s a quick an easy way to do it for iOS: 

func updateTableViewAfterAdditionInNewArray(){

        //Get new feeds and check if counts are different

        let countOfAddedItems = oldArray.count – newArray.count

        

        //If we’re adding information

        if (oldArray) > 0 {

            if debugMode == true{

                print(“We have a difference in counts of \(oldArray)”)

            }

            

            if oldArray.count != 0 {

                //Update data model

                oldArray = newArray

                

                var initialContentOffSet = tableView.contentOffset.y

                //If offset is less than 0 due to refresh up gesture, assume 0

                if initialContentOffSet < 0 {

                    initialContentOffSet = 0

                }

                //Reload, scroll and offset

                tableView.reloadData()

                tableView.scrollToRow(at: IndexPath(row: countOfAddedItems, section: 0), at: .top, animated: false)

                tableView.contentOffset.y = tableView.contentOffset.y  + initialContentOffSet

            }else{

                if debugMode == true{

                    print(“There was no data in oldArray, avoid scrolling. “)

                }

                //Update data model

                oldArray = newArray

                

                tableView.reloadData()

            }

            

        }

 

    }

Hope it helps! I’m really enjoying going back to iOS development, expect more iOS posts this next couple of months as I’m ramping up on my next project. 

Questions / comments / suggestions? I’m at @MarcMasVi 

 

Marc